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