1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.routing;
18
19 import java.util.Collections;
20 import java.util.Map;
21 import java.util.Objects;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.util.concurrent.ConcurrentMap;
24 import java.util.concurrent.TimeUnit;
25
26 import javax.script.Bindings;
27
28 import org.apache.logging.log4j.core.Appender;
29 import org.apache.logging.log4j.core.Filter;
30 import org.apache.logging.log4j.core.LifeCycle2;
31 import org.apache.logging.log4j.core.LogEvent;
32 import org.apache.logging.log4j.core.appender.AbstractAppender;
33 import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
34 import org.apache.logging.log4j.core.config.AppenderControl;
35 import org.apache.logging.log4j.core.config.Configuration;
36 import org.apache.logging.log4j.core.config.Node;
37 import org.apache.logging.log4j.core.config.plugins.Plugin;
38 import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
39 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
40 import org.apache.logging.log4j.core.config.plugins.PluginElement;
41 import org.apache.logging.log4j.core.script.AbstractScript;
42 import org.apache.logging.log4j.core.script.ScriptManager;
43 import org.apache.logging.log4j.core.util.Booleans;
44
45
46
47
48
49
50
51
52
53 @Plugin(name = "Routing", category = "Core", elementType = Appender.ELEMENT_TYPE, printObject = true)
54 public final class RoutingAppender extends AbstractAppender {
55
56 public static final String STATIC_VARIABLES_KEY = "staticVariables";
57
58 public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
59 implements org.apache.logging.log4j.core.util.Builder<RoutingAppender> {
60
61
62 @PluginElement("Script")
63 private AbstractScript defaultRouteScript;
64
65 @PluginElement("Routes")
66 private Routes routes;
67
68 @PluginConfiguration
69 private Configuration configuration;
70
71 @PluginElement("RewritePolicy")
72 private RewritePolicy rewritePolicy;
73
74 @PluginElement("PurgePolicy")
75 private PurgePolicy purgePolicy;
76
77 @Override
78 public RoutingAppender build() {
79 final String name = getName();
80 if (name == null) {
81 LOGGER.error("No name defined for this RoutingAppender");
82 return null;
83 }
84 if (routes == null) {
85 LOGGER.error("No routes defined for RoutingAppender {}", name);
86 return null;
87 }
88 return new RoutingAppender(name, getFilter(), isIgnoreExceptions(), routes, rewritePolicy,
89 configuration, purgePolicy, defaultRouteScript);
90 }
91
92 public Routes getRoutes() {
93 return routes;
94 }
95
96 public Configuration getConfiguration() {
97 return configuration;
98 }
99
100 public AbstractScript getDefaultRouteScript() {
101 return defaultRouteScript;
102 }
103
104 public RewritePolicy getRewritePolicy() {
105 return rewritePolicy;
106 }
107
108 public PurgePolicy getPurgePolicy() {
109 return purgePolicy;
110 }
111
112 public B withRoutes(@SuppressWarnings("hiding") final Routes routes) {
113 this.routes = routes;
114 return asBuilder();
115 }
116
117 public B withConfiguration(@SuppressWarnings("hiding") final Configuration configuration) {
118 this.configuration = configuration;
119 return asBuilder();
120 }
121
122 public B withDefaultRouteScript(@SuppressWarnings("hiding") final AbstractScript defaultRouteScript) {
123 this.defaultRouteScript = defaultRouteScript;
124 return asBuilder();
125 }
126
127 public B withRewritePolicy(@SuppressWarnings("hiding") final RewritePolicy rewritePolicy) {
128 this.rewritePolicy = rewritePolicy;
129 return asBuilder();
130 }
131
132 public void withPurgePolicy(@SuppressWarnings("hiding") final PurgePolicy purgePolicy) {
133 this.purgePolicy = purgePolicy;
134 }
135
136 }
137
138 @PluginBuilderFactory
139 public static <B extends Builder<B>> B newBuilder() {
140 return new Builder<B>().asBuilder();
141 }
142
143 private static final String DEFAULT_KEY = "ROUTING_APPENDER_DEFAULT";
144
145 private final Routes routes;
146 private Route defaultRoute;
147 private final Configuration configuration;
148 private final ConcurrentMap<String, AppenderControl> appenders = new ConcurrentHashMap<>();
149 private final RewritePolicy rewritePolicy;
150 private final PurgePolicy purgePolicy;
151 private final AbstractScript defaultRouteScript;
152 private final ConcurrentMap<Object, Object> scriptStaticVariables = new ConcurrentHashMap<>();
153
154 private RoutingAppender(final String name, final Filter filter, final boolean ignoreExceptions, final Routes routes,
155 final RewritePolicy rewritePolicy, final Configuration configuration, final PurgePolicy purgePolicy,
156 final AbstractScript defaultRouteScript) {
157 super(name, filter, null, ignoreExceptions);
158 this.routes = routes;
159 this.configuration = configuration;
160 this.rewritePolicy = rewritePolicy;
161 this.purgePolicy = purgePolicy;
162 if (this.purgePolicy != null) {
163 this.purgePolicy.initialize(this);
164 }
165 this.defaultRouteScript = defaultRouteScript;
166 Route defRoute = null;
167 for (final Route route : routes.getRoutes()) {
168 if (route.getKey() == null) {
169 if (defRoute == null) {
170 defRoute = route;
171 } else {
172 error("Multiple default routes. Route " + route.toString() + " will be ignored");
173 }
174 }
175 }
176 defaultRoute = defRoute;
177 }
178
179 @Override
180 public void start() {
181 if (defaultRouteScript != null) {
182 if (configuration == null) {
183 error("No Configuration defined for RoutingAppender; required for Script element.");
184 } else {
185 final ScriptManager scriptManager = configuration.getScriptManager();
186 scriptManager.addScript(defaultRouteScript);
187 final Bindings bindings = scriptManager.createBindings(defaultRouteScript);
188 bindings.put(STATIC_VARIABLES_KEY, scriptStaticVariables);
189 final Object object = scriptManager.execute(defaultRouteScript.getName(), bindings);
190 final Route route = routes.getRoute(Objects.toString(object, null));
191 if (route != null) {
192 defaultRoute = route;
193 }
194 }
195 }
196
197 for (final Route route : routes.getRoutes()) {
198 if (route.getAppenderRef() != null) {
199 final Appender appender = configuration.getAppender(route.getAppenderRef());
200 if (appender != null) {
201 final String key = route == defaultRoute ? DEFAULT_KEY : route.getKey();
202 appenders.put(key, new AppenderControl(appender, null, null));
203 } else {
204 error("Appender " + route.getAppenderRef() + " cannot be located. Route ignored");
205 }
206 }
207 }
208 super.start();
209 }
210
211 @Override
212 public boolean stop(final long timeout, final TimeUnit timeUnit) {
213 setStopping();
214 super.stop(timeout, timeUnit, false);
215 final Map<String, Appender> map = configuration.getAppenders();
216 for (final Map.Entry<String, AppenderControl> entry : appenders.entrySet()) {
217 final Appender appender = entry.getValue().getAppender();
218 if (!map.containsKey(appender.getName())) {
219 if (appender instanceof LifeCycle2) {
220 ((LifeCycle2) appender).stop(timeout, timeUnit);
221 } else {
222 appender.stop();
223 }
224 }
225 }
226 setStopped();
227 return true;
228 }
229
230 @Override
231 public void append(LogEvent event) {
232 if (rewritePolicy != null) {
233 event = rewritePolicy.rewrite(event);
234 }
235 final String pattern = routes.getPattern(event, scriptStaticVariables);
236 final String key = pattern != null ? configuration.getStrSubstitutor().replace(event, pattern) : defaultRoute.getKey();
237 final AppenderControl control = getControl(key, event);
238 if (control != null) {
239 control.callAppender(event);
240 }
241
242 if (purgePolicy != null) {
243 purgePolicy.update(key, event);
244 }
245 }
246
247 private synchronized AppenderControl getControl(final String key, final LogEvent event) {
248 AppenderControl control = appenders.get(key);
249 if (control != null) {
250 return control;
251 }
252 Route route = null;
253 for (final Route r : routes.getRoutes()) {
254 if (r.getAppenderRef() == null && key.equals(r.getKey())) {
255 route = r;
256 break;
257 }
258 }
259 if (route == null) {
260 route = defaultRoute;
261 control = appenders.get(DEFAULT_KEY);
262 if (control != null) {
263 return control;
264 }
265 }
266 if (route != null) {
267 final Appender app = createAppender(route, event);
268 if (app == null) {
269 return null;
270 }
271 control = new AppenderControl(app, null, null);
272 appenders.put(key, control);
273 }
274
275 return control;
276 }
277
278 private Appender createAppender(final Route route, final LogEvent event) {
279 final Node routeNode = route.getNode();
280 for (final Node node : routeNode.getChildren()) {
281 if (node.getType().getElementName().equals(Appender.ELEMENT_TYPE)) {
282 final Node appNode = new Node(node);
283 configuration.createConfiguration(appNode, event);
284 if (appNode.getObject() instanceof Appender) {
285 final Appender app = appNode.getObject();
286 app.start();
287 return app;
288 }
289 error("Unable to create Appender of type " + node.getName());
290 return null;
291 }
292 }
293 error("No Appender was configured for route " + route.getKey());
294 return null;
295 }
296
297 public Map<String, AppenderControl> getAppenders() {
298 return Collections.unmodifiableMap(appenders);
299 }
300
301
302
303
304
305
306 public void deleteAppender(final String key) {
307 LOGGER.debug("Deleting route with " + key + " key ");
308 final AppenderControl control = appenders.remove(key);
309 if (null != control) {
310 LOGGER.debug("Stopping route with " + key + " key");
311 control.getAppender().stop();
312 } else {
313 LOGGER.debug("Route with " + key + " key already deleted");
314 }
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329 @Deprecated
330 public static RoutingAppender createAppender(
331 final String name,
332 final String ignore,
333 final Routes routes,
334 final Configuration config,
335 final RewritePolicy rewritePolicy,
336 final PurgePolicy purgePolicy,
337 final Filter filter) {
338
339 final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
340 if (name == null) {
341 LOGGER.error("No name provided for RoutingAppender");
342 return null;
343 }
344 if (routes == null) {
345 LOGGER.error("No routes defined for RoutingAppender");
346 return null;
347 }
348 return new RoutingAppender(name, filter, ignoreExceptions, routes, rewritePolicy, config, purgePolicy, null);
349 }
350
351 public Route getDefaultRoute() {
352 return defaultRoute;
353 }
354
355 public AbstractScript getDefaultRouteScript() {
356 return defaultRouteScript;
357 }
358
359 public PurgePolicy getPurgePolicy() {
360 return purgePolicy;
361 }
362
363 public RewritePolicy getRewritePolicy() {
364 return rewritePolicy;
365 }
366
367 public Routes getRoutes() {
368 return routes;
369 }
370
371 public Configuration getConfiguration() {
372 return configuration;
373 }
374
375 public ConcurrentMap<Object, Object> getScriptStaticVariables() {
376 return scriptStaticVariables;
377 }
378 }