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