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 java.io.IOException;
20 import java.io.InputStream;
21 import java.util.Arrays;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Properties;
26 import java.util.TreeMap;
27
28 import org.apache.logging.log4j.Level;
29 import org.apache.logging.log4j.core.appender.ConsoleAppender;
30 import org.apache.logging.log4j.core.appender.FileAppender;
31 import org.apache.logging.log4j.core.appender.NullAppender;
32 import org.apache.logging.log4j.core.appender.RollingFileAppender;
33 import org.apache.logging.log4j.core.config.ConfigurationException;
34 import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
35 import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
36 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
37 import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
38 import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
39 import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
40 import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
41 import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
42 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
43 import org.apache.logging.log4j.status.StatusLogger;
44 import org.apache.logging.log4j.util.Strings;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class Log4j1ConfigurationParser {
63
64 private static final String COMMA_DELIMITED_RE = "\\s*,\\s*";
65 private static final String ROOTLOGGER = "rootLogger";
66 private static final String ROOTCATEGORY = "rootCategory";
67 private static final String TRUE = "true";
68 private static final String FALSE = "false";
69
70 private final Properties properties = new Properties();
71 private StrSubstitutor strSubstitutorProperties;
72 private StrSubstitutor strSubstitutorSystem;
73
74 private final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory
75 .newConfigurationBuilder();
76
77
78
79
80
81
82
83
84
85
86
87
88 public ConfigurationBuilder<BuiltConfiguration> buildConfigurationBuilder(final InputStream input)
89 throws IOException {
90 try {
91 properties.load(input);
92 strSubstitutorProperties = new StrSubstitutor(properties);
93 strSubstitutorSystem = new StrSubstitutor(System.getProperties());
94 final String rootCategoryValue = getLog4jValue(ROOTCATEGORY);
95 final String rootLoggerValue = getLog4jValue(ROOTLOGGER);
96 if (rootCategoryValue == null && rootLoggerValue == null) {
97
98 warn("Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input);
99
100
101 }
102 builder.setConfigurationName("Log4j1");
103
104 final String debugValue = getLog4jValue("debug");
105 if (Boolean.valueOf(debugValue)) {
106 builder.setStatusLevel(Level.DEBUG);
107 }
108
109 buildRootLogger(getLog4jValue(ROOTCATEGORY));
110 buildRootLogger(getLog4jValue(ROOTLOGGER));
111
112 final Map<String, String> appenderNameToClassName = buildClassToPropertyPrefixMap();
113 for (final Map.Entry<String, String> entry : appenderNameToClassName.entrySet()) {
114 final String appenderName = entry.getKey();
115 final String appenderClass = entry.getValue();
116 buildAppender(appenderName, appenderClass);
117 }
118
119 buildLoggers("log4j.category.");
120 buildLoggers("log4j.logger.");
121 buildProperties();
122 return builder;
123 } catch (final IllegalArgumentException e) {
124 throw new ConfigurationException(e);
125 }
126 }
127
128 private void buildProperties() {
129 for (Map.Entry<Object, Object> entry : new TreeMap<>(properties).entrySet()) {
130 final String key = entry.getKey().toString();
131 if (!key.startsWith("log4j.") && !key.equals(ROOTCATEGORY) && !key.equals(ROOTLOGGER)) {
132 builder.addProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY));
133 }
134 }
135 }
136
137 private void warn(String string) {
138 System.err.println(string);
139 }
140
141 private Map<String, String> buildClassToPropertyPrefixMap() {
142 final String prefix = "log4j.appender.";
143 final int preLength = prefix.length();
144 final Map<String, String> map = new HashMap<>();
145 for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
146 final Object keyObj = entry.getKey();
147 if (keyObj != null) {
148 final String key = keyObj.toString();
149 if (key.startsWith(prefix)) {
150 if (key.indexOf('.', preLength) < 0) {
151 final String name = key.substring(preLength);
152 final Object value = entry.getValue();
153 if (value != null) {
154 map.put(name, value.toString());
155 }
156 }
157 }
158 }
159 }
160 return map;
161 }
162
163 private void buildAppender(final String appenderName, final String appenderClass) {
164 switch (appenderClass) {
165 case "org.apache.log4j.ConsoleAppender":
166 buildConsoleAppender(appenderName);
167 break;
168 case "org.apache.log4j.FileAppender":
169 buildFileAppender(appenderName);
170 break;
171 case "org.apache.log4j.DailyRollingFileAppender":
172 buildDailyRollingFileAppender(appenderName);
173 break;
174 case "org.apache.log4j.RollingFileAppender":
175 buildRollingFileAppender(appenderName);
176 break;
177 case "org.apache.log4j.varia.NullAppender":
178 buildNullAppender(appenderName);
179 break;
180 default:
181 reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName);
182 }
183 }
184
185 private void buildConsoleAppender(final String appenderName) {
186 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, ConsoleAppender.PLUGIN_NAME);
187 final String targetValue = getLog4jAppenderValue(appenderName, "Target", "System.out");
188 if (targetValue != null) {
189 final ConsoleAppender.Target target;
190 switch (targetValue) {
191 case "System.out":
192 target = ConsoleAppender.Target.SYSTEM_OUT;
193 break;
194 case "System.err":
195 target = ConsoleAppender.Target.SYSTEM_ERR;
196 break;
197 default:
198 reportWarning("Unknown value for console Target: " + targetValue);
199 target = null;
200 }
201 if (target != null) {
202 appenderBuilder.addAttribute("target", target);
203 }
204 }
205 buildAttribute(appenderName, appenderBuilder, "Follow", "follow");
206 if (FALSE.equalsIgnoreCase(getLog4jAppenderValue(appenderName, "ImmediateFlush"))) {
207 reportWarning("ImmediateFlush=false is not supported on Console appender");
208 }
209 buildAppenderLayout(appenderName, appenderBuilder);
210 builder.add(appenderBuilder);
211 }
212
213 private void buildFileAppender(final String appenderName) {
214 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, FileAppender.PLUGIN_NAME);
215 buildFileAppender(appenderName, appenderBuilder);
216 builder.add(appenderBuilder);
217 }
218
219 private void buildFileAppender(final String appenderName, final AppenderComponentBuilder appenderBuilder) {
220 buildMandatoryAttribute(appenderName, appenderBuilder, "File", "fileName");
221 buildAttribute(appenderName, appenderBuilder, "Append", "append");
222 buildAttribute(appenderName, appenderBuilder, "BufferedIO", "bufferedIo");
223 buildAttribute(appenderName, appenderBuilder, "BufferSize", "bufferSize");
224 buildAttribute(appenderName, appenderBuilder, "ImmediateFlush", "immediateFlush");
225 buildAppenderLayout(appenderName, appenderBuilder);
226 }
227
228 private void buildDailyRollingFileAppender(final String appenderName) {
229 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
230 RollingFileAppender.PLUGIN_NAME);
231 buildFileAppender(appenderName, appenderBuilder);
232 final String fileName = getLog4jAppenderValue(appenderName, "File");
233 final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", fileName + "'.'yyyy-MM-dd");
234 appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}");
235 final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies")
236 .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true));
237 appenderBuilder.addComponent(triggeringPolicy);
238 appenderBuilder
239 .addComponent(builder.newComponent("DefaultRolloverStrategy").addAttribute("max", Integer.MAX_VALUE));
240 builder.add(appenderBuilder);
241 }
242
243 private void buildRollingFileAppender(final String appenderName) {
244 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
245 RollingFileAppender.PLUGIN_NAME);
246 buildFileAppender(appenderName, appenderBuilder);
247 final String fileName = getLog4jAppenderValue(appenderName, "File");
248 appenderBuilder.addAttribute("filePattern", fileName + ".%i");
249 final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760");
250 final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1");
251 final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies").addComponent(
252 builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString));
253 appenderBuilder.addComponent(triggeringPolicy);
254 appenderBuilder.addComponent(
255 builder.newComponent("DefaultRolloverStrategy").addAttribute("max", maxBackupIndexString));
256 builder.add(appenderBuilder);
257 }
258
259 private void buildAttribute(final String componentName, final ComponentBuilder componentBuilder,
260 final String sourceAttributeName, final String targetAttributeName) {
261 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
262 if (attributeValue != null) {
263 componentBuilder.addAttribute(targetAttributeName, attributeValue);
264 }
265 }
266
267 private void buildAttributeWithDefault(final String componentName, final ComponentBuilder componentBuilder,
268 final String sourceAttributeName, final String targetAttributeName, final String defaultValue) {
269 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName, defaultValue);
270 componentBuilder.addAttribute(targetAttributeName, attributeValue);
271 }
272
273 private void buildMandatoryAttribute(final String componentName, final ComponentBuilder componentBuilder,
274 final String sourceAttributeName, final String targetAttributeName) {
275 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
276 if (attributeValue != null) {
277 componentBuilder.addAttribute(targetAttributeName, attributeValue);
278 } else {
279 reportWarning("Missing " + sourceAttributeName + " for " + componentName);
280 }
281 }
282
283 private void buildNullAppender(String appenderName) {
284 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, NullAppender.PLUGIN_NAME);
285 builder.add(appenderBuilder);
286 }
287
288 private void buildAppenderLayout(final String name, final AppenderComponentBuilder appenderBuilder) {
289 final String layoutClass = getLog4jAppenderValue(name, "layout", null);
290 if (layoutClass != null) {
291 switch (layoutClass) {
292 case "org.apache.log4j.PatternLayout":
293 case "org.apache.log4j.EnhancedPatternLayout": {
294 final String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null)
295
296
297
298
299
300
301 .replace("%x", "%ndc")
302
303
304
305
306
307
308 .replace("%X", "%properties");
309
310 appenderBuilder.add(newPatternLayout(pattern));
311 break;
312 }
313 case "org.apache.log4j.SimpleLayout": {
314 appenderBuilder.add(newPatternLayout("%level - %m%n"));
315 break;
316 }
317 case "org.apache.log4j.TTCCLayout": {
318 String pattern = "%r ";
319 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) {
320 pattern += "[%t] ";
321 }
322 pattern += "%p ";
323 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) {
324 pattern += "%c ";
325 }
326 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) {
327 pattern += "%notEmpty{%ndc }";
328 }
329 pattern += "- %m%n";
330 appenderBuilder.add(newPatternLayout(pattern));
331 break;
332 }
333 case "org.apache.log4j.HTMLLayout": {
334 final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout");
335 htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages"));
336 htmlLayout.addAttribute("locationInfo",
337 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
338 appenderBuilder.add(htmlLayout);
339 break;
340 }
341 case "org.apache.log4j.xml.XMLLayout": {
342 final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout");
343 xmlLayout.addAttribute("locationInfo",
344 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
345 xmlLayout.addAttribute("properties",
346 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE)));
347 appenderBuilder.add(xmlLayout);
348 break;
349 }
350 default:
351 reportWarning("Unknown layout class: " + layoutClass);
352 }
353 }
354 }
355
356 private LayoutComponentBuilder newPatternLayout(final String pattern) {
357 final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout");
358 if (pattern != null) {
359 layoutBuilder.addAttribute("pattern", pattern);
360 }
361 return layoutBuilder;
362 }
363
364 private void buildRootLogger(final String rootLoggerValue) {
365 if (rootLoggerValue == null) {
366 return;
367 }
368 final String[] rootLoggerParts = rootLoggerValue.split(COMMA_DELIMITED_RE);
369 final String rootLoggerLevel = getLevelString(rootLoggerParts, Level.ERROR.name());
370 final RootLoggerComponentBuilder loggerBuilder = builder.newRootLogger(rootLoggerLevel);
371
372 final String[] sortedAppenderNames = Arrays.copyOfRange(rootLoggerParts, 1, rootLoggerParts.length);
373 Arrays.sort(sortedAppenderNames);
374 for (final String appender : sortedAppenderNames) {
375 loggerBuilder.add(builder.newAppenderRef(appender));
376 }
377 builder.add(loggerBuilder);
378 }
379
380 private String getLevelString(final String[] loggerParts, final String defaultLevel) {
381 return loggerParts.length > 0 ? loggerParts[0] : defaultLevel;
382 }
383
384 private void buildLoggers(final String prefix) {
385 final int preLength = prefix.length();
386 for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
387 final Object keyObj = entry.getKey();
388 if (keyObj != null) {
389 final String key = keyObj.toString();
390 if (key.startsWith(prefix)) {
391 final String name = key.substring(preLength);
392 final Object value = entry.getValue();
393 if (value != null) {
394
395 final String valueStr = value.toString();
396 final String[] split = valueStr.split(COMMA_DELIMITED_RE);
397 final String level = getLevelString(split, null);
398 if (level == null) {
399 warn("Level is missing for entry " + entry);
400 } else {
401 final LoggerComponentBuilder newLogger = builder.newLogger(name, level);
402 if (split.length > 1) {
403
404 final String[] sortedAppenderNames = Arrays.copyOfRange(split, 1, split.length);
405 Arrays.sort(sortedAppenderNames);
406 for (String appenderName : sortedAppenderNames) {
407 newLogger.add(builder.newAppenderRef(appenderName));
408 }
409 }
410 builder.add(newLogger);
411 }
412 }
413 }
414 }
415 }
416 }
417
418 private String getLog4jAppenderValue(final String appenderName, final String attributeName) {
419 return getProperty("log4j.appender." + appenderName + "." + attributeName);
420 }
421
422 private String getProperty(final String key) {
423 final String value = properties.getProperty(key);
424 final String sysValue = strSubstitutorSystem.replace(value);
425 return strSubstitutorProperties.replace(sysValue);
426 }
427
428 private String getProperty(final String key, String defaultValue) {
429 final String value = getProperty(key);
430 return value == null ? defaultValue : value;
431 }
432
433 private String getLog4jAppenderValue(final String appenderName, final String attributeName,
434 final String defaultValue) {
435 return getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue);
436 }
437
438 private String getLog4jValue(final String key) {
439 return getProperty("log4j." + key);
440 }
441
442 private void reportWarning(final String msg) {
443 StatusLogger.getLogger().warn("Log4j 1 configuration parser: " + msg);
444 }
445
446 }