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