1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.config;
18
19 import org.apache.logging.log4j.Level;
20 import org.apache.logging.log4j.Logger;
21 import org.apache.logging.log4j.core.Appender;
22 import org.apache.logging.log4j.core.Filter;
23 import org.apache.logging.log4j.core.Layout;
24 import org.apache.logging.log4j.core.LogEvent;
25 import org.apache.logging.log4j.core.appender.ConsoleAppender;
26 import org.apache.logging.log4j.core.config.plugins.PluginAttr;
27 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
28 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
29 import org.apache.logging.log4j.core.config.plugins.PluginManager;
30 import org.apache.logging.log4j.core.config.plugins.PluginElement;
31 import org.apache.logging.log4j.core.config.plugins.PluginNode;
32 import org.apache.logging.log4j.core.config.plugins.PluginType;
33 import org.apache.logging.log4j.core.config.plugins.PluginValue;
34 import org.apache.logging.log4j.core.filter.AbstractFilterable;
35 import org.apache.logging.log4j.core.helpers.NameUtil;
36 import org.apache.logging.log4j.core.layout.PatternLayout;
37 import org.apache.logging.log4j.core.lookup.Interpolator;
38 import org.apache.logging.log4j.core.lookup.MapLookup;
39 import org.apache.logging.log4j.core.lookup.StrLookup;
40 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
41 import org.apache.logging.log4j.status.StatusLogger;
42 import org.apache.logging.log4j.util.PropertiesUtil;
43
44 import java.lang.annotation.Annotation;
45 import java.lang.reflect.Array;
46 import java.lang.reflect.Method;
47 import java.lang.reflect.Modifier;
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.concurrent.ConcurrentMap;
54 import java.util.concurrent.CopyOnWriteArrayList;
55
56
57
58
59 public class BaseConfiguration extends AbstractFilterable implements Configuration {
60
61
62
63 protected static final Logger LOGGER = StatusLogger.getLogger();
64
65
66
67
68 protected Node rootNode;
69
70
71
72
73 protected PluginManager pluginManager;
74
75
76
77
78 protected final List<ConfigurationListener> listeners =
79 new CopyOnWriteArrayList<ConfigurationListener>();
80
81
82
83
84 protected ConfigurationMonitor monitor = new DefaultConfigurationMonitor();
85
86 private String name;
87
88 private ConcurrentMap<String, Appender<?>> appenders = new ConcurrentHashMap<String, Appender<?>>();
89
90 private ConcurrentMap<String, LoggerConfig> loggers = new ConcurrentHashMap<String, LoggerConfig>();
91
92 private final StrLookup tempLookup = new Interpolator();
93
94 private final StrSubstitutor subst = new StrSubstitutor(tempLookup);
95
96 private LoggerConfig root = new LoggerConfig();
97
98 private final boolean started = false;
99
100 private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<String, Object>();
101
102
103
104
105 protected BaseConfiguration() {
106 pluginManager = new PluginManager("Core");
107 rootNode = new Node();
108 }
109
110 public Map<String, String> getProperties() {
111 return (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES);
112 }
113
114
115
116
117 public void start() {
118 pluginManager.collectPlugins();
119 setup();
120 doConfigure();
121 for (final LoggerConfig logger : loggers.values()) {
122 logger.startFilter();
123 }
124 for (final Appender appender : appenders.values()) {
125 appender.start();
126 }
127
128 startFilter();
129 }
130
131
132
133
134 public void stop() {
135 for (final LoggerConfig logger : loggers.values()) {
136 logger.clearAppenders();
137 logger.stopFilter();
138 }
139
140 final Appender[] array = appenders.values().toArray(new Appender[appenders.size()]);
141 for (int i = array.length - 1; i >= 0; --i) {
142 array[i].stop();
143 }
144 stopFilter();
145 }
146
147 protected void setup() {
148 }
149
150 public Object getComponent(final String name) {
151 return componentMap.get(name);
152 }
153
154 public void addComponent(final String name, final Object obj) {
155 componentMap.putIfAbsent(name, obj);
156 }
157
158 protected void doConfigure() {
159 boolean setRoot = false;
160 boolean setLoggers = false;
161 for (final Node child : rootNode.getChildren()) {
162 createConfiguration(child, null);
163 if (child.getObject() == null) {
164 continue;
165 }
166 if (child.getName().equalsIgnoreCase("properties")) {
167 if (tempLookup == subst.getVariableResolver()) {
168 subst.setVariableResolver((StrLookup) child.getObject());
169 } else {
170 LOGGER.error("Properties declaration must be the first element in the configuration");
171 }
172 continue;
173 } else if (tempLookup == subst.getVariableResolver()) {
174 final Map<String, String> map = (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES);
175 final StrLookup lookup = map == null ? null : new MapLookup(map);
176 subst.setVariableResolver(new Interpolator(lookup));
177 }
178 if (child.getName().equalsIgnoreCase("appenders")) {
179 appenders = (ConcurrentMap<String, Appender<?>>) child.getObject();
180 } else if (child.getObject() instanceof Filter) {
181 addFilter((Filter) child.getObject());
182 } else if (child.getName().equalsIgnoreCase("loggers")) {
183 final Loggers l = (Loggers) child.getObject();
184 loggers = l.getMap();
185 setLoggers = true;
186 if (l.getRoot() != null) {
187 root = l.getRoot();
188 setRoot = true;
189 }
190 } else {
191 LOGGER.error("Unknown object \"" + child.getName() + "\" of type " +
192 child.getObject().getClass().getName() + " is ignored");
193 }
194 }
195
196 if (!setLoggers) {
197 LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?");
198 setToDefault();
199 return;
200 } else if (!setRoot) {
201 LOGGER.warn("No Root logger was configured, using default");
202 setToDefault();
203 return;
204 }
205
206 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
207 final LoggerConfig l = entry.getValue();
208 for (final AppenderRef ref : l.getAppenderRefs()) {
209 final Appender app = appenders.get(ref.getRef());
210 if (app != null) {
211 l.addAppender(app, ref.getLevel(), ref.getFilter());
212 } else {
213 LOGGER.error("Unable to locate appender " + ref.getRef() + " for logger " + l.getName());
214 }
215 }
216
217 }
218
219 setParents();
220 }
221
222 private void setToDefault() {
223 setName(DefaultConfiguration.DEFAULT_NAME);
224 final Layout layout = PatternLayout.createLayout("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n",
225 null, null, null);
226 final Appender appender = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false",
227 "true");
228 appender.start();
229 addAppender(appender);
230 final LoggerConfig root = getRootLogger();
231 root.addAppender(appender, null, null);
232
233 final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL);
234 final Level level = levelName != null && Level.valueOf(levelName) != null ?
235 Level.valueOf(levelName) : Level.ERROR;
236 root.setLevel(level);
237 }
238
239 protected PluginManager getPluginManager() {
240 return pluginManager;
241 }
242
243
244
245
246
247 public void setName(final String name) {
248 this.name = name;
249 }
250
251
252
253
254
255 public String getName() {
256 return name;
257 }
258
259
260
261
262
263 public void addListener(final ConfigurationListener listener) {
264 listeners.add(listener);
265 }
266
267
268
269
270
271 public void removeListener(final ConfigurationListener listener) {
272 listeners.remove(listener);
273 }
274
275
276
277
278
279
280 public Appender getAppender(final String name) {
281 return appenders.get(name);
282 }
283
284
285
286
287
288 public Map<String, Appender<?>> getAppenders() {
289 return appenders;
290 }
291
292
293
294
295
296 public void addAppender(final Appender appender) {
297 appenders.put(appender.getName(), appender);
298 }
299
300 public StrSubstitutor getSubst() {
301 return subst;
302 }
303
304 public ConfigurationMonitor getConfigurationMonitor() {
305 return monitor;
306 }
307
308
309
310
311
312
313
314
315
316
317 public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger,
318 final Appender appender) {
319 final String name = logger.getName();
320 appenders.putIfAbsent(name, appender);
321 final LoggerConfig lc = getLoggerConfig(name);
322 if (lc.getName().equals(name)) {
323 lc.addAppender(appender, null, null);
324 } else {
325 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
326 nlc.addAppender(appender, null, null);
327 nlc.setParent(lc);
328 loggers.putIfAbsent(name, nlc);
329 setParents();
330 logger.getContext().updateLoggers();
331 }
332 }
333
334
335
336
337
338
339
340
341
342 public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) {
343 final String name = logger.getName();
344 final LoggerConfig lc = getLoggerConfig(name);
345 if (lc.getName().equals(name)) {
346
347 lc.addFilter(filter);
348 } else {
349 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
350 nlc.addFilter(filter);
351 nlc.setParent(lc);
352 loggers.putIfAbsent(name, nlc);
353 setParents();
354 logger.getContext().updateLoggers();
355 }
356 }
357
358
359
360
361
362
363
364
365
366 public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger,
367 final boolean additive) {
368 final String name = logger.getName();
369 final LoggerConfig lc = getLoggerConfig(name);
370 if (lc.getName().equals(name)) {
371 lc.setAdditive(additive);
372 } else {
373 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), additive);
374 nlc.setParent(lc);
375 loggers.putIfAbsent(name, nlc);
376 setParents();
377 logger.getContext().updateLoggers();
378 }
379 }
380
381
382
383
384
385
386
387 public synchronized void removeAppender(final String name) {
388 for (final LoggerConfig logger : loggers.values()) {
389 logger.removeAppender(name);
390 }
391 final Appender app = appenders.remove(name);
392
393 if (app != null) {
394 app.stop();
395 }
396 }
397
398
399
400
401
402
403
404 public LoggerConfig getLoggerConfig(final String name) {
405 if (loggers.containsKey(name)) {
406 return loggers.get(name);
407 }
408 String substr = name;
409 while ((substr = NameUtil.getSubName(substr)) != null) {
410 if (loggers.containsKey(substr)) {
411 return loggers.get(substr);
412 }
413 }
414 return root;
415 }
416
417
418
419
420
421 public LoggerConfig getRootLogger() {
422 return root;
423 }
424
425
426
427
428
429 public Map<String, LoggerConfig> getLoggers() {
430 return Collections.unmodifiableMap(loggers);
431 }
432
433
434
435
436
437
438 public LoggerConfig getLogger(final String name) {
439 return loggers.get(name);
440 }
441
442
443
444
445
446
447
448
449 public void addLogger(final String name, final LoggerConfig loggerConfig) {
450 if (started) {
451 final String msg = "Cannot add logger " + name + " to an active configuration";
452 LOGGER.warn(msg);
453 throw new IllegalStateException(msg);
454 }
455 loggers.put(name, loggerConfig);
456 setParents();
457 }
458
459
460
461
462
463
464
465 public void removeLogger(final String name) {
466 if (started) {
467 final String msg = "Cannot remove logger " + name + " in an active configuration";
468 LOGGER.warn(msg);
469 throw new IllegalStateException(msg);
470 }
471 loggers.remove(name);
472 setParents();
473 }
474
475 public void createConfiguration(final Node node, final LogEvent event) {
476 final PluginType type = node.getType();
477 if (type != null && type.isDeferChildren()) {
478 node.setObject(createPluginObject(type, node, event));
479 } else {
480 for (final Node child : node.getChildren()) {
481 createConfiguration(child, event);
482 }
483
484 if (type == null) {
485 if (node.getParent() != null) {
486 LOGGER.error("Unable to locate plugin for " + node.getName());
487 }
488 } else {
489 node.setObject(createPluginObject(type, node, event));
490 }
491 }
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509 private Object createPluginObject(final PluginType type, final Node node, final LogEvent event)
510 {
511 final Class clazz = type.getPluginClass();
512
513 if (Map.class.isAssignableFrom(clazz)) {
514 try {
515 final Map<String, Object> map = (Map<String, Object>) clazz.newInstance();
516 for (final Node child : node.getChildren()) {
517 map.put(child.getName(), child.getObject());
518 }
519 return map;
520 } catch (final Exception ex) {
521 LOGGER.warn("Unable to create Map for " + type.getElementName() + " of class " +
522 clazz);
523 }
524 }
525
526 if (List.class.isAssignableFrom(clazz)) {
527 try {
528 final List<Object> list = (List<Object>) clazz.newInstance();
529 for (final Node child : node.getChildren()) {
530 list.add(child.getObject());
531 }
532 return list;
533 } catch (final Exception ex) {
534 LOGGER.warn("Unable to create List for " + type.getElementName() + " of class " +
535 clazz);
536 }
537 }
538
539 Method factoryMethod = null;
540
541 for (final Method method : clazz.getMethods()) {
542 if (method.isAnnotationPresent(PluginFactory.class)) {
543 factoryMethod = method;
544 break;
545 }
546 }
547 if (factoryMethod == null) {
548 return null;
549 }
550
551 final Annotation[][] parmArray = factoryMethod.getParameterAnnotations();
552 final Class[] parmClasses = factoryMethod.getParameterTypes();
553 if (parmArray.length != parmClasses.length) {
554 LOGGER.error("Number of parameter annotations does not equal the number of paramters");
555 }
556 final Object[] parms = new Object[parmClasses.length];
557
558 int index = 0;
559 final Map<String, String> attrs = node.getAttributes();
560 final List<Node> children = node.getChildren();
561 final StringBuilder sb = new StringBuilder();
562 final List<Node> used = new ArrayList<Node>();
563
564
565
566
567
568
569
570
571
572
573
574 for (final Annotation[] parmTypes : parmArray) {
575 for (final Annotation a : parmTypes) {
576 if (sb.length() == 0) {
577 sb.append(" with params(");
578 } else {
579 sb.append(", ");
580 }
581 if (a instanceof PluginNode) {
582 parms[index] = node;
583 sb.append("Node=").append(node.getName());
584 } else if (a instanceof PluginConfiguration) {
585 parms[index] = this;
586 if (this.name != null) {
587 sb.append("Configuration(").append(name).append(")");
588 } else {
589 sb.append("Configuration");
590 }
591 } else if (a instanceof PluginValue) {
592 final String name = ((PluginValue) a).value();
593 String v = node.getValue();
594 if (v == null) {
595 v = getAttrValue("value", attrs);
596 }
597 final String value = subst.replace(event, v);
598 sb.append(name).append("=\"").append(value).append("\"");
599 parms[index] = value;
600 } else if (a instanceof PluginAttr) {
601 final String name = ((PluginAttr) a).value();
602 final String value = subst.replace(event, getAttrValue(name, attrs));
603 sb.append(name).append("=\"").append(value).append("\"");
604 parms[index] = value;
605 } else if (a instanceof PluginElement) {
606 final PluginElement elem = (PluginElement) a;
607 final String name = elem.value();
608 if (parmClasses[index].isArray()) {
609 final Class parmClass = parmClasses[index].getComponentType();
610 final List<Object> list = new ArrayList<Object>();
611 sb.append(name).append("={");
612 boolean first = true;
613 for (final Node child : children) {
614 final PluginType childType = child.getType();
615 if (elem.value().equalsIgnoreCase(childType.getElementName()) ||
616 parmClass.isAssignableFrom(childType.getPluginClass())) {
617 used.add(child);
618 if (!first) {
619 sb.append(", ");
620 }
621 first = false;
622 final Object obj = child.getObject();
623 if (obj == null) {
624 LOGGER.error("Null object returned for " + child.getName() + " in " +
625 node.getName());
626 continue;
627 }
628 if (obj.getClass().isArray()) {
629 printArray(sb, (Object[]) obj);
630 parms[index] = obj;
631 break;
632 }
633 sb.append(child.toString());
634 list.add(obj);
635 }
636 }
637 sb.append("}");
638 if (parms[index] != null) {
639 break;
640 }
641 if (list.size() > 0 && !parmClass.isAssignableFrom(list.get(0).getClass())) {
642 LOGGER.error("Attempted to assign List containing class " +
643 list.get(0).getClass().getName() + " to array of type " + parmClass +
644 " for attribute " + name);
645 break;
646 }
647 final Object[] array = (Object[]) Array.newInstance(parmClass, list.size());
648 int i = 0;
649 for (final Object obj : list) {
650 array[i] = obj;
651 ++i;
652 }
653 parms[index] = array;
654 } else {
655 final Class parmClass = parmClasses[index];
656 boolean present = false;
657 for (final Node child : children) {
658 final PluginType childType = child.getType();
659 if (elem.value().equals(childType.getElementName()) ||
660 parmClass.isAssignableFrom(childType.getPluginClass())) {
661 sb.append(child.getName()).append("(").append(child.toString()).append(")");
662 present = true;
663 used.add(child);
664 parms[index] = child.getObject();
665 break;
666 }
667 }
668 if (!present) {
669 sb.append("null");
670 }
671 }
672 }
673 }
674 ++index;
675 }
676 if (sb.length() > 0) {
677 sb.append(")");
678 }
679
680 if (attrs.size() > 0) {
681 final StringBuilder eb = new StringBuilder();
682 for (final String key : attrs.keySet()) {
683 if (eb.length() == 0) {
684 eb.append(node.getName());
685 eb.append(" contains ");
686 if (attrs.size() == 1) {
687 eb.append("an invalid element or attribute ");
688 } else {
689 eb.append("invalid attributes ");
690 }
691 } else {
692 eb.append(", ");
693 }
694 eb.append("\"");
695 eb.append(key);
696 eb.append("\"");
697
698 }
699 LOGGER.error(eb.toString());
700 }
701
702 if (!type.isDeferChildren() && used.size() != children.size()) {
703 for (final Node child : children) {
704 if (used.contains(child)) {
705 continue;
706 }
707 final String nodeType = node.getType().getElementName();
708 final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + " " + node.getName();
709 LOGGER.error(start + " has no parameter that matches element " + child.getName());
710 }
711 }
712
713 try {
714 final int mod = factoryMethod.getModifiers();
715 if (!Modifier.isStatic(mod)) {
716 LOGGER.error(factoryMethod.getName() + " method is not static on class " +
717 clazz.getName() + " for element " + node.getName());
718 return null;
719 }
720 LOGGER.debug("Calling {} on class {} for element {}{}", factoryMethod.getName(), clazz.getName(),
721 node.getName(), sb.toString());
722
723 return factoryMethod.invoke(null, parms);
724
725
726 } catch (final Exception e) {
727 LOGGER.error("Unable to invoke method " + factoryMethod.getName() + " in class " +
728 clazz.getName() + " for element " + node.getName(), e);
729 }
730 return null;
731 }
732
733 private void printArray(final StringBuilder sb, final Object... array) {
734 boolean first = true;
735 for (final Object obj : array) {
736 if (!first) {
737 sb.append(", ");
738 }
739 sb.append(obj.toString());
740 first = false;
741 }
742 }
743
744 private String getAttrValue(final String name, final Map<String, String> attrs) {
745 for (final String key : attrs.keySet()) {
746 if (key.equalsIgnoreCase(name)) {
747 final String attr = attrs.get(key);
748 attrs.remove(key);
749 return attr;
750 }
751 }
752 return null;
753 }
754
755 private void setParents() {
756 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
757 final LoggerConfig logger = entry.getValue();
758 String name = entry.getKey();
759 if (!name.equals("")) {
760 final int i = name.lastIndexOf('.');
761 if (i > 0) {
762 name = name.substring(0, i);
763 LoggerConfig parent = getLoggerConfig(name);
764 if (parent == null) {
765 parent = root;
766 }
767 logger.setParent(parent);
768 }
769 }
770 }
771 }
772 }