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.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.logging.log4j.Level;
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Marker;
29 import org.apache.logging.log4j.core.Appender;
30 import org.apache.logging.log4j.core.Core;
31 import org.apache.logging.log4j.core.Filter;
32 import org.apache.logging.log4j.core.LogEvent;
33 import org.apache.logging.log4j.core.LoggerContext;
34 import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
35 import org.apache.logging.log4j.core.async.AsyncLoggerContext;
36 import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
37 import org.apache.logging.log4j.core.config.plugins.Plugin;
38 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
39 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
40 import org.apache.logging.log4j.core.config.plugins.PluginElement;
41 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
42 import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
43 import org.apache.logging.log4j.core.filter.AbstractFilterable;
44 import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
45 import org.apache.logging.log4j.core.impl.LocationAware;
46 import org.apache.logging.log4j.core.impl.LocationAwareLogEventFactory;
47 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
48 import org.apache.logging.log4j.core.impl.LogEventFactory;
49 import org.apache.logging.log4j.core.impl.ReusableLogEventFactory;
50 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
51 import org.apache.logging.log4j.core.util.Booleans;
52 import org.apache.logging.log4j.core.util.Constants;
53 import org.apache.logging.log4j.core.util.Loader;
54 import org.apache.logging.log4j.message.Message;
55 import org.apache.logging.log4j.util.PerformanceSensitive;
56 import org.apache.logging.log4j.util.PropertiesUtil;
57 import org.apache.logging.log4j.util.StackLocatorUtil;
58 import org.apache.logging.log4j.util.Strings;
59
60
61
62
63 @Plugin(name = "logger", category = Node.CATEGORY, printObject = true)
64 public class LoggerConfig extends AbstractFilterable implements LocationAware {
65
66 public static final String ROOT = "root";
67 private static LogEventFactory LOG_EVENT_FACTORY = null;
68
69 private List<AppenderRef> appenderRefs = new ArrayList<>();
70 private final AppenderControlArraySet appenders = new AppenderControlArraySet();
71 private final String name;
72 private LogEventFactory logEventFactory;
73 private Level level;
74 private boolean additive = true;
75 private boolean includeLocation = true;
76 private LoggerConfig parent;
77 private Map<Property, Boolean> propertiesMap;
78 private final List<Property> properties;
79 private final boolean propertiesRequireLookup;
80 private final Configuration config;
81 private final ReliabilityStrategy reliabilityStrategy;
82
83 static {
84 final String factory = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_LOG_EVENT_FACTORY);
85 if (factory != null) {
86 try {
87 final Class<?> clazz = Loader.loadClass(factory);
88 if (clazz != null && LogEventFactory.class.isAssignableFrom(clazz)) {
89 LOG_EVENT_FACTORY = (LogEventFactory) clazz.newInstance();
90 }
91 } catch (final Exception ex) {
92 LOGGER.error("Unable to create LogEventFactory {}", factory, ex);
93 }
94 }
95 if (LOG_EVENT_FACTORY == null) {
96 LOG_EVENT_FACTORY = Constants.ENABLE_THREADLOCALS
97 ? new ReusableLogEventFactory()
98 : new DefaultLogEventFactory();
99 }
100 }
101
102
103
104
105 public LoggerConfig() {
106 this.logEventFactory = LOG_EVENT_FACTORY;
107 this.level = Level.ERROR;
108 this.name = Strings.EMPTY;
109 this.properties = null;
110 this.propertiesRequireLookup = false;
111 this.config = null;
112 this.reliabilityStrategy = new DefaultReliabilityStrategy(this);
113 }
114
115
116
117
118
119
120
121
122 public LoggerConfig(final String name, final Level level, final boolean additive) {
123 this.logEventFactory = LOG_EVENT_FACTORY;
124 this.name = name;
125 this.level = level;
126 this.additive = additive;
127 this.properties = null;
128 this.propertiesRequireLookup = false;
129 this.config = null;
130 this.reliabilityStrategy = new DefaultReliabilityStrategy(this);
131 }
132
133 protected LoggerConfig(final String name, final List<AppenderRef> appenders, final Filter filter,
134 final Level level, final boolean additive, final Property[] properties, final Configuration config,
135 final boolean includeLocation) {
136 super(filter);
137 this.logEventFactory = LOG_EVENT_FACTORY;
138 this.name = name;
139 this.appenderRefs = appenders;
140 this.level = level;
141 this.additive = additive;
142 this.includeLocation = includeLocation;
143 this.config = config;
144 if (properties != null && properties.length > 0) {
145 this.properties = Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(
146 properties, properties.length)));
147 } else {
148 this.properties = null;
149 }
150 this.propertiesRequireLookup = containsPropertyRequiringLookup(properties);
151 this.reliabilityStrategy = config.getReliabilityStrategy(this);
152 }
153
154 private static boolean containsPropertyRequiringLookup(final Property[] properties) {
155 if (properties == null) {
156 return false;
157 }
158 for (int i = 0; i < properties.length; i++) {
159 if (properties[i].isValueNeedsLookup()) {
160 return true;
161 }
162 }
163 return false;
164 }
165
166 @Override
167 public Filter getFilter() {
168 return super.getFilter();
169 }
170
171
172
173
174
175
176 public String getName() {
177 return name;
178 }
179
180
181
182
183
184
185 public void setParent(final LoggerConfig parent) {
186 this.parent = parent;
187 }
188
189
190
191
192
193
194 public LoggerConfig getParent() {
195 return this.parent;
196 }
197
198
199
200
201
202
203
204
205 public void addAppender(final Appender appender, final Level level, final Filter filter) {
206 appenders.add(new AppenderControl(appender, level, filter));
207 }
208
209
210
211
212
213
214 public void removeAppender(final String name) {
215 AppenderControl removed = null;
216 while ((removed = appenders.remove(name)) != null) {
217 cleanupFilter(removed);
218 }
219 }
220
221
222
223
224
225
226 public Map<String, Appender> getAppenders() {
227 return appenders.asMap();
228 }
229
230
231
232
233 protected void clearAppenders() {
234 do {
235 final AppenderControl[] original = appenders.clear();
236 for (final AppenderControl ctl : original) {
237 cleanupFilter(ctl);
238 }
239 } while (!appenders.isEmpty());
240 }
241
242 private void cleanupFilter(final AppenderControl ctl) {
243 final Filter filter = ctl.getFilter();
244 if (filter != null) {
245 ctl.removeFilter(filter);
246 filter.stop();
247 }
248 }
249
250
251
252
253
254
255 public List<AppenderRef> getAppenderRefs() {
256 return appenderRefs;
257 }
258
259
260
261
262
263
264 public void setLevel(final Level level) {
265 this.level = level;
266 }
267
268
269
270
271
272
273 public Level getLevel() {
274 return level == null ? parent == null ? Level.ERROR : parent.getLevel() : level;
275 }
276
277
278
279
280
281
282 public LogEventFactory getLogEventFactory() {
283 return logEventFactory;
284 }
285
286
287
288
289
290
291 public void setLogEventFactory(final LogEventFactory logEventFactory) {
292 this.logEventFactory = logEventFactory;
293 }
294
295
296
297
298
299
300 public boolean isAdditive() {
301 return additive;
302 }
303
304
305
306
307
308
309 public void setAdditive(final boolean additive) {
310 this.additive = additive;
311 }
312
313
314
315
316
317
318
319 public boolean isIncludeLocation() {
320 return includeLocation;
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336 @Deprecated
337 public Map<Property, Boolean> getProperties() {
338 if (properties == null) {
339 return null;
340 }
341 if (propertiesMap == null) {
342 final Map<Property, Boolean> result = new HashMap<>(properties.size() * 2);
343 for (int i = 0; i < properties.size(); i++) {
344 result.put(properties.get(i), Boolean.valueOf(properties.get(i).isValueNeedsLookup()));
345 }
346 propertiesMap = Collections.unmodifiableMap(result);
347 }
348 return propertiesMap;
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362
363 public List<Property> getPropertyList() {
364 return properties;
365 }
366
367 public boolean isPropertiesRequireLookup() {
368 return propertiesRequireLookup;
369 }
370
371
372
373
374
375
376
377
378
379
380
381 @PerformanceSensitive("allocation")
382 public void log(final String loggerName, final String fqcn, final Marker marker, final Level level,
383 final Message data, final Throwable t) {
384 List<Property> props = null;
385 if (!propertiesRequireLookup) {
386 props = properties;
387 } else {
388 if (properties != null) {
389 props = new ArrayList<>(properties.size());
390 final LogEvent event = Log4jLogEvent.newBuilder()
391 .setMessage(data)
392 .setMarker(marker)
393 .setLevel(level)
394 .setLoggerName(loggerName)
395 .setLoggerFqcn(fqcn)
396 .setThrown(t)
397 .build();
398 for (int i = 0; i < properties.size(); i++) {
399 final Property prop = properties.get(i);
400 final String value = prop.isValueNeedsLookup()
401 ? config.getStrSubstitutor().replace(event, prop.getValue())
402 : prop.getValue();
403 props.add(Property.createProperty(prop.getName(), value));
404 }
405 }
406 }
407 final LogEvent logEvent = logEventFactory instanceof LocationAwareLogEventFactory ?
408 ((LocationAwareLogEventFactory) logEventFactory).createEvent(loggerName, marker, fqcn, requiresLocation() ?
409 StackLocatorUtil.calcLocation(fqcn) : null, level, data, props, t) :
410 logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
411 try {
412 log(logEvent, LoggerConfigPredicate.ALL);
413 } finally {
414
415 ReusableLogEventFactory.release(logEvent);
416 }
417 }
418
419
420
421
422
423
424
425
426
427
428
429
430 @PerformanceSensitive("allocation")
431 public void log(final String loggerName, final String fqcn, final StackTraceElement location, final Marker marker,
432 final Level level, final Message data, final Throwable t) {
433 List<Property> props = null;
434 if (!propertiesRequireLookup) {
435 props = properties;
436 } else {
437 if (properties != null) {
438 props = new ArrayList<>(properties.size());
439 final LogEvent event = Log4jLogEvent.newBuilder()
440 .setMessage(data)
441 .setMarker(marker)
442 .setLevel(level)
443 .setLoggerName(loggerName)
444 .setLoggerFqcn(fqcn)
445 .setThrown(t)
446 .build();
447 for (int i = 0; i < properties.size(); i++) {
448 final Property prop = properties.get(i);
449 final String value = prop.isValueNeedsLookup()
450 ? config.getStrSubstitutor().replace(event, prop.getValue())
451 : prop.getValue();
452 props.add(Property.createProperty(prop.getName(), value));
453 }
454 }
455 }
456 final LogEvent logEvent = logEventFactory instanceof LocationAwareLogEventFactory ?
457 ((LocationAwareLogEventFactory) logEventFactory).createEvent(loggerName, marker, fqcn, location, level,
458 data, props, t) : logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
459 try {
460 log(logEvent, LoggerConfigPredicate.ALL);
461 } finally {
462
463 ReusableLogEventFactory.release(logEvent);
464 }
465 }
466
467
468
469
470
471
472 public void log(final LogEvent event) {
473 log(event, LoggerConfigPredicate.ALL);
474 }
475
476
477
478
479
480
481
482
483 protected void log(final LogEvent event, final LoggerConfigPredicate predicate) {
484 if (!isFiltered(event)) {
485 processLogEvent(event, predicate);
486 }
487 }
488
489
490
491
492
493
494
495 public ReliabilityStrategy getReliabilityStrategy() {
496 return reliabilityStrategy;
497 }
498
499 private void processLogEvent(final LogEvent event, final LoggerConfigPredicate predicate) {
500 event.setIncludeLocation(isIncludeLocation());
501 if (predicate.allow(this)) {
502 callAppenders(event);
503 }
504 logParent(event, predicate);
505 }
506
507 public boolean requiresLocation() {
508 if (!includeLocation) {
509 return false;
510 }
511 AppenderControl[] controls = appenders.get();
512 LoggerConfig loggerConfig = this;
513 while (loggerConfig != null) {
514 for (AppenderControl control : controls) {
515 Appender appender = control.getAppender();
516 if (appender instanceof LocationAware && ((LocationAware) appender).requiresLocation()) {
517 return true;
518 }
519 }
520 if (loggerConfig.additive) {
521 loggerConfig = loggerConfig.parent;
522 if (loggerConfig != null) {
523 controls = loggerConfig.appenders.get();
524 }
525 } else {
526 break;
527 }
528 }
529 return false;
530 }
531
532 private void logParent(final LogEvent event, final LoggerConfigPredicate predicate) {
533 if (additive && parent != null) {
534 parent.log(event, predicate);
535 }
536 }
537
538 @PerformanceSensitive("allocation")
539 protected void callAppenders(final LogEvent event) {
540 final AppenderControl[] controls = appenders.get();
541
542 for (int i = 0; i < controls.length; i++) {
543 controls[i].callAppender(event);
544 }
545 }
546
547 @Override
548 public String toString() {
549 return Strings.isEmpty(name) ? ROOT : name;
550 }
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566 @Deprecated
567 public static LoggerConfig createLogger(final String additivity,
568
569 final Level level,
570 @PluginAttribute("name") final String loggerName,
571 final String includeLocation,
572 final AppenderRef[] refs,
573 final Property[] properties,
574 @PluginConfiguration final Configuration config,
575 final Filter filter) {
576
577 if (loggerName == null) {
578 LOGGER.error("Loggers cannot be configured without a name");
579 return null;
580 }
581
582 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
583 final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName;
584 final boolean additive = Booleans.parseBoolean(additivity, true);
585
586 return new LoggerConfig(name, appenderRefs, filter, level, additive, properties, config,
587 includeLocation(includeLocation, config));
588 }
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604 @PluginFactory
605 public static LoggerConfig createLogger(
606
607 @PluginAttribute(value = "additivity", defaultBoolean = true) final boolean additivity,
608 @PluginAttribute("level") final Level level,
609 @Required(message = "Loggers cannot be configured without a name") @PluginAttribute("name") final String loggerName,
610 @PluginAttribute("includeLocation") final String includeLocation,
611 @PluginElement("AppenderRef") final AppenderRef[] refs,
612 @PluginElement("Properties") final Property[] properties,
613 @PluginConfiguration final Configuration config,
614 @PluginElement("Filter") final Filter filter
615
616 ) {
617 final String name = loggerName.equals(ROOT) ? Strings.EMPTY : loggerName;
618 return new LoggerConfig(name, Arrays.asList(refs), filter, level, additivity, properties, config,
619 includeLocation(includeLocation, config));
620 }
621
622
623
624
625 @Deprecated
626 protected static boolean includeLocation(final String includeLocationConfigValue) {
627 return includeLocation(includeLocationConfigValue, null);
628 }
629
630
631
632 protected static boolean includeLocation(final String includeLocationConfigValue, final Configuration configuration) {
633 if (includeLocationConfigValue == null) {
634 LoggerContext context = null;
635 if (configuration != null) {
636 context = configuration.getLoggerContext();
637 }
638 if (context != null) {
639 return !(context instanceof AsyncLoggerContext);
640 } else {
641 return !AsyncLoggerContextSelector.isSelected();
642 }
643 }
644 return Boolean.parseBoolean(includeLocationConfigValue);
645 }
646
647 protected final boolean hasAppenders() {
648 return !appenders.isEmpty();
649 }
650
651
652
653
654 @Plugin(name = ROOT, category = Core.CATEGORY_NAME, printObject = true)
655 public static class RootLogger extends LoggerConfig {
656
657 @PluginFactory
658 public static LoggerConfig createLogger(
659
660 @PluginAttribute("additivity") final String additivity,
661 @PluginAttribute("level") final Level level,
662 @PluginAttribute("includeLocation") final String includeLocation,
663 @PluginElement("AppenderRef") final AppenderRef[] refs,
664 @PluginElement("Properties") final Property[] properties,
665 @PluginConfiguration final Configuration config,
666 @PluginElement("Filter") final Filter filter) {
667
668 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
669 final Level actualLevel = level == null ? Level.ERROR : level;
670 final boolean additive = Booleans.parseBoolean(additivity, true);
671
672 return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, actualLevel, additive,
673 properties, config, includeLocation(includeLocation, config));
674 }
675 }
676
677 protected enum LoggerConfigPredicate {
678 ALL() {
679 @Override
680 boolean allow(final LoggerConfig config) {
681 return true;
682 }
683 },
684 ASYNCHRONOUS_ONLY() {
685 @Override
686 boolean allow(final LoggerConfig config) {
687 return config instanceof AsyncLoggerConfig;
688 }
689 },
690 SYNCHRONOUS_ONLY() {
691 @Override
692 boolean allow(final LoggerConfig config) {
693 return !ASYNCHRONOUS_ONLY.allow(config);
694 }
695 };
696
697 abstract boolean allow(LoggerConfig config);
698 }
699 }