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