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