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