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