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