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