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