1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.log4j.config;
18
19 import org.apache.log4j.Appender;
20 import org.apache.log4j.Layout;
21 import org.apache.log4j.LogManager;
22 import org.apache.log4j.PatternLayout;
23 import org.apache.log4j.bridge.AppenderAdapter;
24 import org.apache.log4j.bridge.AppenderWrapper;
25 import org.apache.log4j.builders.BuilderManager;
26 import org.apache.log4j.helpers.OptionConverter;
27 import org.apache.log4j.spi.ErrorHandler;
28 import org.apache.log4j.spi.Filter;
29 import org.apache.logging.log4j.core.LoggerContext;
30 import org.apache.logging.log4j.core.config.ConfigurationSource;
31 import org.apache.logging.log4j.core.config.LoggerConfig;
32 import org.apache.logging.log4j.core.config.status.StatusConfiguration;
33 import org.apache.logging.log4j.util.LoaderUtil;
34
35 import java.io.InputStream;
36 import java.lang.reflect.InvocationTargetException;
37 import java.util.ArrayList;
38 import java.util.Enumeration;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Properties;
43 import java.util.SortedMap;
44 import java.util.StringTokenizer;
45 import java.util.TreeMap;
46
47
48
49
50 public class PropertiesConfiguration extends Log4j1Configuration {
51
52 private static final String CATEGORY_PREFIX = "log4j.category.";
53 private static final String LOGGER_PREFIX = "log4j.logger.";
54 private static final String ADDITIVITY_PREFIX = "log4j.additivity.";
55 private static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
56 private static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
57 private static final String APPENDER_PREFIX = "log4j.appender.";
58 private static final String LOGGER_REF = "logger-ref";
59 private static final String ROOT_REF = "root-ref";
60 private static final String APPENDER_REF_TAG = "appender-ref";
61 public static final long DEFAULT_DELAY = 60000;
62 public static final String DEBUG_KEY="log4j.debug";
63
64 private static final String INTERNAL_ROOT_NAME = "root";
65
66 private final Map<String, Appender> registry;
67
68
69
70
71
72
73
74 public PropertiesConfiguration(final LoggerContext loggerContext, final ConfigurationSource source,
75 int monitorIntervalSeconds) {
76 super(loggerContext, source, monitorIntervalSeconds);
77 registry = new HashMap<>();
78 }
79
80 public void doConfigure() {
81 InputStream is = getConfigurationSource().getInputStream();
82 Properties props = new Properties();
83 try {
84 props.load(is);
85 } catch (Exception e) {
86 LOGGER.error("Could not read configuration file [{}].", getConfigurationSource().toString(), e);
87 return;
88 }
89
90 doConfigure(props);
91 }
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266 private void doConfigure(Properties properties) {
267 String status = "error";
268 String value = properties.getProperty(DEBUG_KEY);
269 if (value == null) {
270 value = properties.getProperty("log4j.configDebug");
271 if (value != null) {
272 LOGGER.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
273 }
274 }
275
276 if (value != null) {
277 status = OptionConverter.toBoolean(value, false) ? "debug" : "error";
278 }
279
280 final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status);
281 statusConfig.initialize();
282
283 configureRoot(properties);
284 parseLoggers(properties);
285
286 LOGGER.debug("Finished configuring.");
287 }
288
289
290
291
292
293 private void configureRoot(Properties props) {
294 String effectiveFrefix = ROOT_LOGGER_PREFIX;
295 String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
296
297 if (value == null) {
298 value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
299 effectiveFrefix = ROOT_CATEGORY_PREFIX;
300 }
301
302 if (value == null) {
303 LOGGER.debug("Could not find root logger information. Is this OK?");
304 } else {
305 LoggerConfig root = getRootLogger();
306 parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
307 }
308 }
309
310
311
312
313 private void parseLoggers(Properties props) {
314 Enumeration enumeration = props.propertyNames();
315 while (enumeration.hasMoreElements()) {
316 String key = (String) enumeration.nextElement();
317 if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
318 String loggerName = null;
319 if (key.startsWith(CATEGORY_PREFIX)) {
320 loggerName = key.substring(CATEGORY_PREFIX.length());
321 } else if (key.startsWith(LOGGER_PREFIX)) {
322 loggerName = key.substring(LOGGER_PREFIX.length());
323 }
324 String value = OptionConverter.findAndSubst(key, props);
325 LoggerConfig loggerConfig = getLogger(loggerName);
326 if (loggerConfig == null) {
327 boolean additivity = getAdditivityForLogger(props, loggerName);
328 loggerConfig = new LoggerConfig(loggerName, org.apache.logging.log4j.Level.ERROR, additivity);
329 addLogger(loggerName, loggerConfig);
330 }
331 parseLogger(props, loggerConfig, key, loggerName, value);
332 }
333 }
334 }
335
336
337
338
339 private boolean getAdditivityForLogger(Properties props, String loggerName) {
340 boolean additivity = true;
341 String key = ADDITIVITY_PREFIX + loggerName;
342 String value = OptionConverter.findAndSubst(key, props);
343 LOGGER.debug("Handling {}=[{}]", key, value);
344
345 if ((value != null) && (!value.equals(""))) {
346 additivity = OptionConverter.toBoolean(value, true);
347 }
348 return additivity;
349 }
350
351
352
353
354 private void parseLogger(Properties props, LoggerConfig logger, String optionKey, String loggerName, String value) {
355
356 LOGGER.debug("Parsing for [{}] with value=[{}].", loggerName, value);
357
358 StringTokenizer st = new StringTokenizer(value, ",");
359
360
361
362 if (!(value.startsWith(",") || value.equals(""))) {
363
364
365 if (!st.hasMoreTokens()) {
366 return;
367 }
368
369 String levelStr = st.nextToken();
370 LOGGER.debug("Level token is [{}].", levelStr);
371
372 org.apache.logging.log4j.Level level = levelStr == null ? org.apache.logging.log4j.Level.ERROR :
373 OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
374 logger.setLevel(level);
375 LOGGER.debug("Logger {} level set to {}", loggerName, level);
376 }
377
378 Appender appender;
379 String appenderName;
380 while (st.hasMoreTokens()) {
381 appenderName = st.nextToken().trim();
382 if (appenderName == null || appenderName.equals(",")) {
383 continue;
384 }
385 LOGGER.debug("Parsing appender named \"{}\".", appenderName);
386 appender = parseAppender(props, appenderName);
387 if (appender != null) {
388 LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName,
389 logger.getName());
390 logger.addAppender(getAppender(appenderName), null, null);
391 } else {
392 LOGGER.debug("Appender named [{}}] not found.", appenderName);
393 }
394 }
395 }
396
397 public Appender parseAppender(Properties props, String appenderName) {
398 Appender appender = registry.get(appenderName);
399 if ((appender != null)) {
400 LOGGER.debug("Appender \"" + appenderName + "\" was already parsed.");
401 return appender;
402 }
403
404 final String prefix = APPENDER_PREFIX + appenderName;
405 final String layoutPrefix = prefix + ".layout";
406 final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
407 String className = OptionConverter.findAndSubst(prefix, props);
408 appender = manager.parseAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props, this);
409 if (appender == null) {
410 appender = buildAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props);
411 } else {
412 registry.put(appenderName, appender);
413 if (appender instanceof AppenderWrapper) {
414 addAppender(((AppenderWrapper) appender).getAppender());
415 } else {
416 addAppender(new AppenderAdapter(appender).getAdapter());
417 }
418 }
419 return appender;
420 }
421
422 private Appender buildAppender(final String appenderName, final String className, final String prefix,
423 final String layoutPrefix, final String filterPrefix, final Properties props) {
424 Appender appender = newInstanceOf(className, "Appender");
425 if (appender == null) {
426 return null;
427 }
428 appender.setName(appenderName);
429 appender.setLayout(parseLayout(layoutPrefix, appenderName, props));
430 final String errorHandlerPrefix = prefix + ".errorhandler";
431 String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
432 if (errorHandlerClass != null) {
433 ErrorHandler eh = parseErrorHandler(props, errorHandlerPrefix, errorHandlerClass, appender);
434 if (eh != null) {
435 appender.setErrorHandler(eh);
436 }
437 }
438 parseAppenderFilters(props, filterPrefix, appenderName);
439 String[] keys = new String[] {
440 layoutPrefix,
441 };
442 addProperties(appender, keys, props, prefix);
443 if (appender instanceof AppenderWrapper) {
444 addAppender(((AppenderWrapper) appender).getAppender());
445 } else {
446 addAppender(new AppenderAdapter(appender).getAdapter());
447 }
448 registry.put(appenderName, appender);
449 return appender;
450 }
451
452 public Layout parseLayout(String layoutPrefix, String appenderName, Properties props) {
453 String layoutClass = OptionConverter.findAndSubst(layoutPrefix, props);
454 if (layoutClass == null) {
455 return null;
456 }
457 Layout layout = manager.parseLayout(layoutClass, layoutPrefix, props, this);
458 if (layout == null) {
459 layout = buildLayout(layoutPrefix, layoutClass, appenderName, props);
460 }
461 return layout;
462 }
463
464 private Layout buildLayout(String layoutPrefix, String className, String appenderName, Properties props) {
465 Layout layout = newInstanceOf(className, "Layout");
466 if (layout == null) {
467 return null;
468 }
469 LOGGER.debug("Parsing layout options for \"{}\".", appenderName);
470 PropertySetter.setProperties(layout, props, layoutPrefix + ".");
471 LOGGER.debug("End of parsing for \"{}\".", appenderName);
472 return layout;
473 }
474
475 public ErrorHandler parseErrorHandler(final Properties props, final String errorHandlerPrefix,
476 final String errorHandlerClass, final Appender appender) {
477 ErrorHandler eh = newInstanceOf(errorHandlerClass, "ErrorHandler");
478 final String[] keys = new String[] {
479 errorHandlerPrefix + "." + ROOT_REF,
480 errorHandlerPrefix + "." + LOGGER_REF,
481 errorHandlerPrefix + "." + APPENDER_REF_TAG
482 };
483 addProperties(eh, keys, props, errorHandlerPrefix);
484 return eh;
485 }
486
487 public void addProperties(final Object obj, final String[] keys, final Properties props, final String prefix) {
488 final Properties edited = new Properties();
489 props.stringPropertyNames().stream().filter((name) -> {
490 if (name.startsWith(prefix)) {
491 for (String key : keys) {
492 if (name.equals(key)) {
493 return false;
494 }
495 }
496 return true;
497 }
498 return false;
499 }).forEach((name) -> edited.put(name, props.getProperty(name)));
500 PropertySetter.setProperties(obj, edited, prefix + ".");
501 }
502
503
504 public Filter parseAppenderFilters(Properties props, String filterPrefix, String appenderName) {
505
506
507
508 int fIdx = filterPrefix.length();
509 SortedMap<String, List<NameValue>> filters = new TreeMap<>();
510 Enumeration e = props.keys();
511 String name = "";
512 while (e.hasMoreElements()) {
513 String key = (String) e.nextElement();
514 if (key.startsWith(filterPrefix)) {
515 int dotIdx = key.indexOf('.', fIdx);
516 String filterKey = key;
517 if (dotIdx != -1) {
518 filterKey = key.substring(0, dotIdx);
519 name = key.substring(dotIdx + 1);
520 }
521 List<NameValue> filterOpts = filters.computeIfAbsent(filterKey, k -> new ArrayList<>());
522 if (dotIdx != -1) {
523 String value = OptionConverter.findAndSubst(key, props);
524 filterOpts.add(new NameValue(name, value));
525 }
526 }
527 }
528
529 Filter head = null;
530 Filter next = null;
531 for (Map.Entry<String, List<NameValue>> entry : filters.entrySet()) {
532 String clazz = props.getProperty(entry.getKey());
533 Filter filter = null;
534 if (clazz != null) {
535 filter = manager.parseFilter(clazz, filterPrefix, props, this);
536 if (filter == null) {
537 LOGGER.debug("Filter key: [{}] class: [{}] props: {}", entry.getKey(), clazz, entry.getValue());
538 filter = buildFilter(clazz, appenderName, entry.getValue());
539 }
540 }
541 if (filter != null) {
542 if (head != null) {
543 head = filter;
544 next = filter;
545 } else {
546 next.setNext(filter);
547 next = filter;
548 }
549 }
550 }
551 return head;
552 }
553
554 private Filter buildFilter(String className, String appenderName, List<NameValue> props) {
555 Filter filter = newInstanceOf(className, "Filter");
556 if (filter != null) {
557 PropertySetter propSetter = new PropertySetter(filter);
558 for (NameValue property : props) {
559 propSetter.setProperty(property.key, property.value);
560 }
561 propSetter.activate();
562 }
563 return filter;
564 }
565
566
567 private static <T> T newInstanceOf(String className, String type) {
568 try {
569 return LoaderUtil.newInstanceOf(className);
570 } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException |
571 InstantiationException | InvocationTargetException ex) {
572 LOGGER.error("Unable to create {} {} due to {}:{}", type, className,
573 ex.getClass().getSimpleName(), ex.getMessage());
574 return null;
575 }
576 }
577
578 private static class NameValue {
579 String key, value;
580
581 NameValue(String key, String value) {
582 this.key = key;
583 this.value = value;
584 }
585
586 public String toString() {
587 return key + "=" + value;
588 }
589 }
590
591 }