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