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.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.TimeUnit;
29 import java.util.concurrent.atomic.AtomicBoolean;
30 import java.util.concurrent.atomic.AtomicInteger;
31 import java.util.concurrent.locks.Condition;
32 import java.util.concurrent.locks.Lock;
33 import java.util.concurrent.locks.ReentrantLock;
34
35 import org.apache.logging.log4j.Level;
36 import org.apache.logging.log4j.LogManager;
37 import org.apache.logging.log4j.Marker;
38 import org.apache.logging.log4j.core.Appender;
39 import org.apache.logging.log4j.core.Filter;
40 import org.apache.logging.log4j.core.LogEvent;
41 import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
42 import org.apache.logging.log4j.core.config.plugins.Plugin;
43 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
44 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
45 import org.apache.logging.log4j.core.config.plugins.PluginElement;
46 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
47 import org.apache.logging.log4j.core.filter.AbstractFilterable;
48 import org.apache.logging.log4j.core.impl.DefaultLogEventFactory;
49 import org.apache.logging.log4j.core.impl.LogEventFactory;
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.PropertiesUtil;
56 import org.apache.logging.log4j.util.Strings;
57
58
59
60
61 @Plugin(name = "logger", category = "Core", printObject = true)
62 public class LoggerConfig extends AbstractFilterable {
63
64 private static final int MAX_RETRIES = 3;
65 private static LogEventFactory LOG_EVENT_FACTORY = null;
66
67 private List<AppenderRef> appenderRefs = new ArrayList<AppenderRef>();
68 private final Map<String, AppenderControl> appenders = new ConcurrentHashMap<String, AppenderControl>();
69 private final String name;
70 private LogEventFactory logEventFactory;
71 private Level level;
72 private boolean additive = true;
73 private boolean includeLocation = true;
74 private LoggerConfig parent;
75 private final AtomicInteger counter = new AtomicInteger();
76 private final AtomicBoolean shutdown = new AtomicBoolean(false);
77 private final Map<Property, Boolean> properties;
78 private final Configuration config;
79 private final Lock shutdownLock = new ReentrantLock();
80 private final Condition noLogEvents = shutdownLock.newCondition();
81
82 static {
83 final String factory = PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_LOG_EVENT_FACTORY);
84 if (factory != null) {
85 try {
86 final Class<?> clazz = Loader.loadClass(factory);
87 if (clazz != null && LogEventFactory.class.isAssignableFrom(clazz)) {
88 LOG_EVENT_FACTORY = (LogEventFactory) clazz.newInstance();
89 }
90 } catch (final Exception ex) {
91 LOGGER.error("Unable to create LogEventFactory {}", factory, ex);
92 }
93 }
94 if (LOG_EVENT_FACTORY == null) {
95 LOG_EVENT_FACTORY = new DefaultLogEventFactory();
96 }
97 }
98
99
100
101
102 public LoggerConfig() {
103 this.logEventFactory = LOG_EVENT_FACTORY;
104 this.level = Level.ERROR;
105 this.name = Strings.EMPTY;
106 this.properties = null;
107 this.config = null;
108 }
109
110
111
112
113
114
115
116
117 public LoggerConfig(final String name, final Level level,
118 final boolean additive) {
119 this.logEventFactory = LOG_EVENT_FACTORY;
120 this.name = name;
121 this.level = level;
122 this.additive = additive;
123 this.properties = null;
124 this.config = null;
125 }
126
127 protected LoggerConfig(final String name,
128 final List<AppenderRef> appenders, final Filter filter,
129 final Level level, final boolean additive,
130 final Property[] properties, final Configuration config,
131 final boolean includeLocation) {
132 super(filter);
133 this.logEventFactory = LOG_EVENT_FACTORY;
134 this.name = name;
135 this.appenderRefs = appenders;
136 this.level = level;
137 this.additive = additive;
138 this.includeLocation = includeLocation;
139 this.config = config;
140 if (properties != null && properties.length > 0) {
141 this.properties = new HashMap<Property, Boolean>(properties.length);
142 for (final Property prop : properties) {
143 final boolean interpolate = prop.getValue().contains("${");
144 this.properties.put(prop, interpolate);
145 }
146 } else {
147 this.properties = null;
148 }
149 }
150
151 @Override
152 public Filter getFilter() {
153 return super.getFilter();
154 }
155
156
157
158
159
160
161 public String getName() {
162 return name;
163 }
164
165
166
167
168
169
170 public void setParent(final LoggerConfig parent) {
171 this.parent = parent;
172 }
173
174
175
176
177
178
179 public LoggerConfig getParent() {
180 return this.parent;
181 }
182
183
184
185
186
187
188
189
190 public void addAppender(final Appender appender, final Level level,
191 final Filter filter) {
192 appenders.put(appender.getName(), new AppenderControl(appender, level,
193 filter));
194 }
195
196
197
198
199
200
201 public void removeAppender(final String name) {
202 final AppenderControl ctl = appenders.remove(name);
203 if (ctl != null) {
204 cleanupFilter(ctl);
205 }
206 }
207
208
209
210
211
212
213
214 public Map<String, Appender> getAppenders() {
215 final Map<String, Appender> map = new HashMap<String, Appender>();
216 for (final Map.Entry<String, AppenderControl> entry : appenders
217 .entrySet()) {
218 map.put(entry.getKey(), entry.getValue().getAppender());
219 }
220 return map;
221 }
222
223
224
225
226 protected void clearAppenders() {
227 waitForCompletion();
228 final Collection<AppenderControl> controls = appenders.values();
229 final Iterator<AppenderControl> iterator = controls.iterator();
230 while (iterator.hasNext()) {
231 final AppenderControl ctl = iterator.next();
232 iterator.remove();
233 cleanupFilter(ctl);
234 }
235 }
236
237 private void cleanupFilter(final AppenderControl ctl) {
238 final Filter filter = ctl.getFilter();
239 if (filter != null) {
240 ctl.removeFilter(filter);
241 filter.stop();
242 }
243 }
244
245
246
247
248
249
250 public List<AppenderRef> getAppenderRefs() {
251 return appenderRefs;
252 }
253
254
255
256
257
258
259 public void setLevel(final Level level) {
260 this.level = level;
261 }
262
263
264
265
266
267
268 public Level getLevel() {
269 return level == null ? parent.getLevel() : level;
270 }
271
272
273
274
275
276
277 public LogEventFactory getLogEventFactory() {
278 return logEventFactory;
279 }
280
281
282
283
284
285
286
287 public void setLogEventFactory(final LogEventFactory logEventFactory) {
288 this.logEventFactory = logEventFactory;
289 }
290
291
292
293
294
295
296 public boolean isAdditive() {
297 return additive;
298 }
299
300
301
302
303
304
305
306 public void setAdditive(final boolean additive) {
307 this.additive = additive;
308 }
309
310
311
312
313
314
315
316
317 public boolean isIncludeLocation() {
318 return includeLocation;
319 }
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 public Map<Property, Boolean> getProperties() {
336 return properties == null ? null : Collections
337 .unmodifiableMap(properties);
338 }
339
340
341
342
343
344
345
346
347
348
349
350 public void log(final String loggerName, final String fqcn,
351 final Marker marker, final Level level, final Message data,
352 final Throwable t) {
353 List<Property> props = null;
354 if (properties != null) {
355 props = new ArrayList<Property>(properties.size());
356
357 for (final Map.Entry<Property, Boolean> entry : properties.entrySet()) {
358 final Property prop = entry.getKey();
359 final String value = entry.getValue() ? config.getStrSubstitutor()
360 .replace(prop.getValue()) : prop.getValue();
361 props.add(Property.createProperty(prop.getName(), value));
362 }
363 }
364 final LogEvent event = logEventFactory.createEvent(loggerName, marker, fqcn, level, data, props, t);
365 log(event);
366 }
367
368
369
370
371
372 private void waitForCompletion() {
373 shutdownLock.lock();
374 try {
375 if (shutdown.compareAndSet(false, true)) {
376 int retries = 0;
377 while (counter.get() > 0) {
378 try {
379 noLogEvents.await(retries + 1, TimeUnit.SECONDS);
380 } catch (final InterruptedException ie) {
381 if (++retries > MAX_RETRIES) {
382 break;
383 }
384 }
385 }
386 }
387 } finally {
388 shutdownLock.unlock();
389 }
390 }
391
392
393
394
395
396
397 public void log(final LogEvent event) {
398
399 counter.incrementAndGet();
400 try {
401 if (isFiltered(event)) {
402 return;
403 }
404
405 event.setIncludeLocation(isIncludeLocation());
406
407 callAppenders(event);
408
409 if (additive && parent != null) {
410 parent.log(event);
411 }
412 } finally {
413 if (counter.decrementAndGet() == 0) {
414 shutdownLock.lock();
415 try {
416 if (shutdown.get()) {
417 noLogEvents.signalAll();
418 }
419 } finally {
420 shutdownLock.unlock();
421 }
422 }
423 }
424 }
425
426 protected void callAppenders(final LogEvent event) {
427 for (final AppenderControl control : appenders.values()) {
428 control.callAppender(event);
429 }
430 }
431
432
433 @Override
434 public String toString() {
435 return Strings.isEmpty(name) ? "root" : name;
436 }
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451 @PluginFactory
452 public static LoggerConfig createLogger(
453 @PluginAttribute("additivity") final String additivity,
454 @PluginAttribute("level") final Level level,
455 @PluginAttribute("name") final String loggerName,
456 @PluginAttribute("includeLocation") final String includeLocation,
457 @PluginElement("AppenderRef") final AppenderRef[] refs,
458 @PluginElement("Properties") final Property[] properties,
459 @PluginConfiguration final Configuration config,
460 @PluginElement("Filter") final Filter filter) {
461 if (loggerName == null) {
462 LOGGER.error("Loggers cannot be configured without a name");
463 return null;
464 }
465
466 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
467 final String name = loggerName.equals("root") ? Strings.EMPTY : loggerName;
468 final boolean additive = Booleans.parseBoolean(additivity, true);
469
470 return new LoggerConfig(name, appenderRefs, filter, level, additive,
471 properties, config, includeLocation(includeLocation));
472 }
473
474
475
476 protected static boolean includeLocation(final String includeLocationConfigValue) {
477 if (includeLocationConfigValue == null) {
478 final boolean sync = !AsyncLoggerContextSelector.class.getName()
479 .equals(System.getProperty(Constants.LOG4J_CONTEXT_SELECTOR));
480 return sync;
481 }
482 return Boolean.parseBoolean(includeLocationConfigValue);
483 }
484
485
486
487
488 @Plugin(name = "root", category = "Core", printObject = true)
489 public static class RootLogger extends LoggerConfig {
490
491 @PluginFactory
492 public static LoggerConfig createLogger(
493 @PluginAttribute("additivity") final String additivity,
494 @PluginAttribute("level") final Level level,
495 @PluginAttribute("includeLocation") final String includeLocation,
496 @PluginElement("AppenderRef") final AppenderRef[] refs,
497 @PluginElement("Properties") final Property[] properties,
498 @PluginConfiguration final Configuration config,
499 @PluginElement("Filter") final Filter filter) {
500 final List<AppenderRef> appenderRefs = Arrays.asList(refs);
501 final Level actualLevel = level == null ? Level.ERROR : level;
502 final boolean additive = Booleans.parseBoolean(additivity, true);
503
504 return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs,
505 filter, actualLevel, additive, properties, config,
506 includeLocation(includeLocation));
507 }
508 }
509
510 }