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