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