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