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