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