001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package com.randomnoun.common.log4j2; 018 019import java.beans.BeanInfo; 020import java.beans.IntrospectionException; 021import java.beans.Introspector; 022import java.beans.PropertyDescriptor; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.StringWriter; 026import java.util.Arrays; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.Map.Entry; 030import java.util.Objects; 031import java.util.Properties; 032import java.util.TreeMap; 033 034import org.apache.logging.log4j.Level; 035import org.apache.logging.log4j.core.appender.ConsoleAppender; 036import org.apache.logging.log4j.core.appender.FileAppender; 037import org.apache.logging.log4j.core.appender.NullAppender; 038import org.apache.logging.log4j.core.appender.RollingFileAppender; 039import org.apache.logging.log4j.core.config.ConfigurationException; 040import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; 041import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; 042import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; 043import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; 044import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; 045import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; 046import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; 047import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; 048import org.apache.logging.log4j.core.config.plugins.Plugin; 049import org.apache.logging.log4j.core.lookup.StrSubstitutor; 050import org.apache.logging.log4j.status.StatusLogger; 051import org.apache.logging.log4j.util.Strings; 052 053/** 054 * You know how log4j 1 can be configured via XML or property files, and 055 * how log4j 2 can be configured via maliciously and arbitrarily different XML or property files ? 056 * 057 * <p>This class allows you to configure log4j2 using log4j1-style property configuration files, 058 * which allows you to slowly migrate your clusterfuck of logging frameworks from 059 * log4j1 to log4j2 without having to redo your entire logging configuration, which by 060 * now is Turing-complete and giving recipe suggestions. 061 * 062 * <p>It's based on the org.apache.log4j.config.Log4j1ConfigurationParser that's bundled in the 063 * log4j-1.2-api artifact ( specifically, org.apache.logging.log4j:log4j-1.2-api:2.14.0 ), but: 064 * 065 * <ul><li>buildConfigurationBuilder() can take a Properties object (as well as an InputStream) 066 * <li>Unknown log4j1 appenders are constructed (and wrapped in some kind of log4j2 adapter) instead of ignored 067 * <li>Unknown log4j2 appenders are constructed instead of ignored 068 * </ul> 069 * 070 * <p>The original Log4j1ConfigurationParser also converts a handful of "well-known" appenders 071 * into their log4j2 equivalents, which still happens in this class. 072 * 073 * <p>To use, run the following: 074 * <pre> 075Log4j1ConfigurationParser lcp = new Log4j1ConfigurationParser(); 076ConfigurationBuilder<?> builder = lcp.buildConfigurationBuilder(props); 077Configuration config = builder.build(); 078Configurator.reconfigure(config); 079 * </pre> 080 * 081 * Original comment block: 082 * 083 * Experimental parser for Log4j 1.2 properties configuration files. 084 * 085 * This class is not thread-safe. 086 * 087 * <p> 088 * From the Log4j 1.2 Javadocs: 089 * </p> 090 * <p> 091 * All option values admit variable substitution. The syntax of variable substitution is similar to that of Unix shells. The string between 092 * an opening "${" and closing "}" is interpreted as a key. The value of the substituted variable can be defined as a system property or in 093 * the configuration file itself. The value of the key is first searched in the system properties, and if not found there, it is then 094 * searched in the configuration file being parsed. The corresponding value replaces the ${variableName} sequence. For example, if java.home 095 * system property is set to /home/xyz, then every occurrence of the sequence ${java.home} will be interpreted as /home/xyz. 096 * </p> 097 * 098 * <p>Changes from the Log4j1ConfigurationParser in org.apache.logging.log4j:log4j-1.2-api:2.14.0 099 * <ul><li>buildConfigurationBuilder() can take a Properties object (as well as an InputStream) 100 * <li>Unknown log4j2 appenders are constructed instead of ignored 101 * </ul> 102 */ 103public class Log4j1ConfigurationParser { 104 105 private static final String COMMA_DELIMITED_RE = "\\s*,\\s*"; 106 private static final String ROOTLOGGER = "rootLogger"; 107 private static final String ROOTCATEGORY = "rootCategory"; 108 private static final String TRUE = "true"; 109 private static final String FALSE = "false"; 110 111 private final Properties properties = new Properties(); 112 private StrSubstitutor strSubstitutorProperties; 113 private StrSubstitutor strSubstitutorSystem; 114 115 private final ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory 116 .newConfigurationBuilder(); 117 118 /** 119 * Parses a Log4j 1.2 properties configuration file in ISO 8859-1 encoding into a ConfigurationBuilder. 120 * 121 * @param input 122 * InputStream to read from is assumed to be ISO 8859-1, and will not be closed. 123 * @return the populated ConfigurationBuilder, never {@literal null} 124 * @throws IOException 125 * if unable to read the input 126 * @throws ConfigurationException 127 * if the input does not contain a valid configuration 128 */ 129 public ConfigurationBuilder<BuiltConfiguration> buildConfigurationBuilder(final InputStream input) 130 throws IOException 131 { 132 try { 133 properties.load(input); 134 _buildConfigurationBuilder(); 135 return builder; 136 } catch (final IllegalArgumentException e) { 137 throw new ConfigurationException(e); 138 } 139 140 } 141 142 public ConfigurationBuilder<BuiltConfiguration> buildConfigurationBuilder(Properties properties) { 143 try { 144 this.properties.putAll(properties); 145 _buildConfigurationBuilder(); 146 return builder; 147 } catch (final IllegalArgumentException e) { 148 throw new ConfigurationException(e); 149 } 150 } 151 152 protected ConfigurationBuilder<BuiltConfiguration> _buildConfigurationBuilder() { 153 try { 154 strSubstitutorProperties = new StrSubstitutor(properties); 155 strSubstitutorSystem = new StrSubstitutor(System.getProperties()); 156 final String rootCategoryValue = getLog4jValue(ROOTCATEGORY); 157 final String rootLoggerValue = getLog4jValue(ROOTLOGGER); 158 if (rootCategoryValue == null && rootLoggerValue == null) { 159 // This is not a Log4j 1 properties configuration file. 160 warn("Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in properties"); 161 // throw new ConfigurationException( 162 // "Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input); 163 } 164 builder.setConfigurationName("Log4j1"); 165 // DEBUG 166 final String debugValue = getLog4jValue("debug"); 167 if (Boolean.valueOf(debugValue)) { 168 builder.setStatusLevel(Level.DEBUG); 169 } 170 // Root 171 buildRootLogger(getLog4jValue(ROOTCATEGORY)); 172 buildRootLogger(getLog4jValue(ROOTLOGGER)); 173 // Appenders 174 final Map<String, String> appenderNameToClassName = buildClassToPropertyPrefixMap(); 175 for (final Map.Entry<String, String> entry : appenderNameToClassName.entrySet()) { 176 final String appenderName = entry.getKey(); 177 final String appenderClass = entry.getValue(); 178 buildAppender(appenderName, appenderClass); 179 } 180 // Loggers 181 buildLoggers("log4j.category."); 182 buildLoggers("log4j.logger."); 183 buildProperties(); 184 return builder; 185 } catch (final IllegalArgumentException e) { 186 throw new ConfigurationException(e); 187 } 188 } 189 190 191 private void buildProperties() { 192 for (final Map.Entry<Object, Object> entry : new TreeMap<Object, Object>(properties).entrySet()) { 193 final String key = entry.getKey().toString(); 194 if (!key.startsWith("log4j.") && !key.equals(ROOTCATEGORY) && !key.equals(ROOTLOGGER)) { 195 builder.addProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY)); 196 } 197 } 198 } 199 200 private void warn(final String string) { 201 System.err.println(string); 202 } 203 204 private Map<String, String> buildClassToPropertyPrefixMap() { 205 final String prefix = "log4j.appender."; 206 final int preLength = prefix.length(); 207 final Map<String, String> map = new HashMap<String, String>(); 208 for (final Map.Entry<Object, Object> entry : properties.entrySet()) { 209 final Object keyObj = entry.getKey(); 210 if (keyObj != null) { 211 final String key = keyObj.toString(); 212 if (key.startsWith(prefix)) { 213 if (key.indexOf('.', preLength) < 0) { 214 final String name = key.substring(preLength); 215 final Object value = entry.getValue(); 216 if (value != null) { 217 map.put(name, value.toString()); 218 } 219 } 220 } 221 } 222 } 223 return map; 224 } 225 226 private void buildAppender(final String appenderName, final String appenderClass) { 227 if ("org.apache.log4j.ConsoleAppender".equals(appenderClass)) { 228 buildConsoleAppender(appenderName); 229 } else if ("org.apache.log4j.FileAppender".equals(appenderClass)) { 230 buildFileAppender(appenderName); 231 } else if ("org.apache.log4j.DailyRollingFileAppender".equals(appenderClass)) { 232 buildDailyRollingFileAppender(appenderName); 233 } else if ("org.apache.log4j.RollingFileAppender".equals(appenderClass)) { 234 buildRollingFileAppender(appenderName); 235 } else if ("org.apache.log4j.varia.NullAppender".equals(appenderClass)) { 236 buildNullAppender(appenderName); 237 } else { 238 // old Log4j1ConfigurationParser did this: 239 // reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName); 240 // which isn't helpful 241 242 Class<?> c; 243 try { 244 c = Class.forName(appenderClass); 245 } catch (ClassNotFoundException e) { 246 throw new ConfigurationException("Cannot find appender class '" + appenderClass + "'", e); 247 } 248 // org.apache.log4j.ConsoleAppender c1; 249 if (org.apache.log4j.Appender.class.isAssignableFrom(c)) { 250 /* log4j1 appender, use an adapter once I fathom how that's supposed to work */ 251 // throw new ConfigurationException("Cannot handle log4j1 appender class '" + appenderClass + "'"); 252 253 /* 254 // construct the log4j1 appender. this is soooooooooooo going to work. 255 final String prefix = "log4j.appender." + appenderName; 256 final String layoutPrefix = "log4j.appender." + appenderName + ".layout"; 257 final String filterPrefix = "log4j.appender." + appenderName + ".filter."; 258 final org.apache.log4j.Appender log4j1Appender = buildLog4j1Appender(appenderName, c.getName(), prefix, layoutPrefix, filterPrefix, properties); 259 260 // this should work but doesn't because DefaultComponentBuilder.addAttribute() converts every value it receives into a string, 261 // when then has to be converted back into an object using a TypeConverter (which needs it's own TypeConverter registry), 262 // and a log4j1 appender won't round-trip through that properly. 263 264 AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, Log4j1WrapperAppender.PLUGIN_NAME); 265 appenderBuilder.addAttribute("log4j1Appender", log4j1Appender); 266 builder.add(appenderBuilder); 267 // ^ the attribute collection in that builder is a Map<String, String> 268 269 // so not only that, you can't create your own "non-default" DefaultComponentBuilder 270 // because the Components that these builders are building are also composed of Strings. 271 // I suppose you could create a Component which consists of the entire property file that we're configuring this with 272 // and then extract the appender properties out of that, but frankly, this is the point at which I'm giving up. 273 */ 274 275 // ok, so now the Log4jWrapperAppender constructs the appender, we don't construct it here 276 277 StringWriter w = new StringWriter(); 278 try { 279 properties.store(w, null); 280 } catch (IOException e) { 281 throw new IllegalStateException("IOException in StringWriter"); 282 } 283 284 AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, Log4j1WrapperAppender.PLUGIN_NAME); 285 appenderBuilder.addAttribute("className", c.getName()); 286 appenderBuilder.addAttribute("properties", w.toString()); 287 builder.add(appenderBuilder); 288 289 290 } else if (org.apache.logging.log4j.core.Appender.class.isAssignableFrom(c)) { 291 /* log4j2 appender, because wheels aren't going to reinvent themselves */ 292 293 // no idea what these are for or why they're necessary, if they are, which they probably aren't 294 Plugin log4jPlugin = c.getAnnotation(org.apache.logging.log4j.core.config.plugins.Plugin.class); 295 String pluginName = log4jPlugin.name(); 296 if (pluginName == null) { 297 pluginName = "generatedPluginName_" + c.getName(); 298 } 299 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, pluginName); 300 301 // set the props ( from "log4j.appender." + appenderName + "." + attributeName ); 302 // we're checking for getter/setter methods on the Appender to work out what's settable 303 // even though we end up using a Builder class to construct the thing for some reason 304 BeanInfo b; 305 try { 306 b = Introspector.getBeanInfo(c); 307 } catch (IntrospectionException e1) { 308 throw new ConfigurationException("Could not determine properties of appender class '" + appenderClass + "'", e1); 309 } 310 PropertyDescriptor[] pds = b.getPropertyDescriptors(); 311 Map<String, PropertyDescriptor> pdMap = new HashMap<String, PropertyDescriptor>(); 312 for (PropertyDescriptor pd : pds) { 313 pdMap.put(pd.getName().toLowerCase(), pd); // case-insensitive match 314 } 315 String prefix = "log4j.appender." + appenderName + "."; 316 for (Entry<Object, Object> e : properties.entrySet()) { 317 String k = (String) e.getKey(); 318 String v = (String) e.getValue().toString(); 319 if (k.startsWith(prefix)) { 320 String rest = k.substring(prefix.length()); 321 PropertyDescriptor pd = pdMap.get(rest.toLowerCase()); 322 if (pd == null) { 323 throw new ConfigurationException("Appender class '" + appenderClass + "' has no property '" + rest + "'"); 324 } else if (pd.getPropertyType().equals(String.class)) { 325 appenderBuilder.addAttribute(pd.getName(), v); 326 } else if (pd.getPropertyType().equals(Integer.class) || pd.getPropertyType().equals(int.class)) { 327 appenderBuilder.addAttribute(pd.getName(), Integer.valueOf(v).intValue()); 328 } else { 329 // all the other types, but let's see if Strings work, which they won't. 330 appenderBuilder.addAttribute(pd.getName(), v); 331 } 332 } 333 } 334 335 builder.add(appenderBuilder); 336 } 337 } 338 } 339 340 341 private void buildConsoleAppender(final String appenderName) { 342 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, ConsoleAppender.PLUGIN_NAME); 343 final String targetValue = getLog4jAppenderValue(appenderName, "Target", "System.out"); 344 if (targetValue != null) { 345 final ConsoleAppender.Target target; 346 if (targetValue.equals("System.out")) { 347 target = ConsoleAppender.Target.SYSTEM_OUT; 348 } else if (targetValue.equals("System.err")) { 349 target = ConsoleAppender.Target.SYSTEM_ERR; 350 } else { 351 reportWarning("Unknown value for console Target: " + targetValue); 352 target = null; 353 } 354 if (target != null) { 355 appenderBuilder.addAttribute("target", target); 356 } 357 } 358 buildAttribute(appenderName, appenderBuilder, "Follow", "follow"); 359 if (FALSE.equalsIgnoreCase(getLog4jAppenderValue(appenderName, "ImmediateFlush"))) { 360 reportWarning("ImmediateFlush=false is not supported on Console appender"); 361 } 362 buildAppenderLayout(appenderName, appenderBuilder); 363 builder.add(appenderBuilder); 364 } 365 366 private void buildFileAppender(final String appenderName) { 367 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, FileAppender.PLUGIN_NAME); 368 buildFileAppender(appenderName, appenderBuilder); 369 builder.add(appenderBuilder); 370 } 371 372 private void buildFileAppender(final String appenderName, final AppenderComponentBuilder appenderBuilder) { 373 buildMandatoryAttribute(appenderName, appenderBuilder, "File", "fileName"); 374 buildAttribute(appenderName, appenderBuilder, "Append", "append"); 375 buildAttribute(appenderName, appenderBuilder, "BufferedIO", "bufferedIo"); 376 buildAttribute(appenderName, appenderBuilder, "BufferSize", "bufferSize"); 377 buildAttribute(appenderName, appenderBuilder, "ImmediateFlush", "immediateFlush"); 378 buildAppenderLayout(appenderName, appenderBuilder); 379 } 380 381 private void buildDailyRollingFileAppender(final String appenderName) { 382 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, 383 RollingFileAppender.PLUGIN_NAME); 384 buildFileAppender(appenderName, appenderBuilder); 385 final String fileName = getLog4jAppenderValue(appenderName, "File"); 386 final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", fileName + "'.'yyyy-MM-dd"); 387 appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}"); 388 final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies") 389 .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true)); 390 appenderBuilder.addComponent(triggeringPolicy); 391 appenderBuilder 392 .addComponent(builder.newComponent("DefaultRolloverStrategy").addAttribute("max", Integer.MAX_VALUE)); 393 builder.add(appenderBuilder); 394 } 395 396 private void buildRollingFileAppender(final String appenderName) { 397 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, 398 RollingFileAppender.PLUGIN_NAME); 399 buildFileAppender(appenderName, appenderBuilder); 400 final String fileName = getLog4jAppenderValue(appenderName, "File"); 401 appenderBuilder.addAttribute("filePattern", fileName + ".%i"); 402 final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760"); 403 final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1"); 404 final ComponentBuilder<?> triggeringPolicy = builder.newComponent("Policies").addComponent( 405 builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString)); 406 appenderBuilder.addComponent(triggeringPolicy); 407 appenderBuilder.addComponent( 408 builder.newComponent("DefaultRolloverStrategy").addAttribute("max", maxBackupIndexString)); 409 builder.add(appenderBuilder); 410 } 411 412 private void buildAttribute(final String componentName, final ComponentBuilder componentBuilder, 413 final String sourceAttributeName, final String targetAttributeName) { 414 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); 415 if (attributeValue != null) { 416 componentBuilder.addAttribute(targetAttributeName, attributeValue); 417 } 418 } 419 420 private void buildAttributeWithDefault(final String componentName, final ComponentBuilder componentBuilder, 421 final String sourceAttributeName, final String targetAttributeName, final String defaultValue) { 422 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName, defaultValue); 423 componentBuilder.addAttribute(targetAttributeName, attributeValue); 424 } 425 426 private void buildMandatoryAttribute(final String componentName, final ComponentBuilder componentBuilder, 427 final String sourceAttributeName, final String targetAttributeName) { 428 final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); 429 if (attributeValue != null) { 430 componentBuilder.addAttribute(targetAttributeName, attributeValue); 431 } else { 432 reportWarning("Missing " + sourceAttributeName + " for " + componentName); 433 } 434 } 435 436 private void buildNullAppender(final String appenderName) { 437 final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, NullAppender.PLUGIN_NAME); 438 builder.add(appenderBuilder); 439 } 440 441 private void buildAppenderLayout(final String name, final AppenderComponentBuilder appenderBuilder) { 442 final String layoutClass = getLog4jAppenderValue(name, "layout", null); 443 if (layoutClass != null) { 444 if ("org.apache.log4j.PatternLayout".equals(layoutClass) || 445 "org.apache.log4j.EnhancedPatternLayout".equals(layoutClass)) { 446 final String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null) 447 448 // Log4j 2's %x (NDC) is not compatible with Log4j 1's 449 // %x 450 // Log4j 1: "foo bar baz" 451 // Log4j 2: "[foo, bar, baz]" 452 // Use %ndc to get the Log4j 1 format 453 .replace("%x", "%ndc") 454 455 // Log4j 2's %X (MDC) is not compatible with Log4j 1's 456 // %X 457 // Log4j 1: "{{foo,bar}{hoo,boo}}" 458 // Log4j 2: "{foo=bar,hoo=boo}" 459 // Use %properties to get the Log4j 1 format 460 .replace("%X", "%properties"); 461 462 appenderBuilder.add(newPatternLayout(pattern)); 463 } else if ("org.apache.log4j.SimpleLayout".equals(layoutClass)) { 464 appenderBuilder.add(newPatternLayout("%level - %m%n")); 465 } else if ("org.apache.log4j.TTCCLayout".equals(layoutClass)) { 466 String pattern = "%r "; 467 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) { 468 pattern += "[%t] "; 469 } 470 pattern += "%p "; 471 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) { 472 pattern += "%c "; 473 } 474 if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) { 475 pattern += "%notEmpty{%ndc }"; 476 } 477 pattern += "- %m%n"; 478 appenderBuilder.add(newPatternLayout(pattern)); 479 480 } else if ("org.apache.log4j.HTMLLayout".equals(layoutClass)) { 481 final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout"); 482 htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages")); 483 htmlLayout.addAttribute("locationInfo", 484 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); 485 appenderBuilder.add(htmlLayout); 486 } else if ("org.apache.log4j.xml.XMLLayout".equals(layoutClass)) { 487 final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout"); 488 xmlLayout.addAttribute("locationInfo", 489 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); 490 xmlLayout.addAttribute("properties", 491 Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE))); 492 appenderBuilder.add(xmlLayout); 493 } else { 494 reportWarning("Unknown layout class: " + layoutClass); 495 } 496 } 497 } 498 499 private LayoutComponentBuilder newPatternLayout(final String pattern) { 500 final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout"); 501 if (pattern != null) { 502 layoutBuilder.addAttribute("pattern", pattern); 503 } 504 return layoutBuilder; 505 } 506 507 private void buildRootLogger(final String rootLoggerValue) { 508 if (rootLoggerValue == null) { 509 return; 510 } 511 final String[] rootLoggerParts = rootLoggerValue.split(COMMA_DELIMITED_RE); 512 final String rootLoggerLevel = getLevelString(rootLoggerParts, Level.ERROR.name()); 513 final RootLoggerComponentBuilder loggerBuilder = builder.newRootLogger(rootLoggerLevel); 514 // 515 final String[] sortedAppenderNames = Arrays.copyOfRange(rootLoggerParts, 1, rootLoggerParts.length); 516 Arrays.sort(sortedAppenderNames); 517 for (final String appender : sortedAppenderNames) { 518 loggerBuilder.add(builder.newAppenderRef(appender)); 519 } 520 builder.add(loggerBuilder); 521 } 522 523 private String getLevelString(final String[] loggerParts, final String defaultLevel) { 524 return loggerParts.length > 0 ? loggerParts[0] : defaultLevel; 525 } 526 527 private void buildLoggers(final String prefix) { 528 final int preLength = prefix.length(); 529 for (final Map.Entry<Object, Object> entry : properties.entrySet()) { 530 final Object keyObj = entry.getKey(); 531 if (keyObj != null) { 532 final String key = keyObj.toString(); 533 if (key.startsWith(prefix)) { 534 final String name = key.substring(preLength); 535 final Object value = entry.getValue(); 536 if (value != null) { 537 // a Level may be followed by a list of Appender refs. 538 final String valueStr = value.toString(); 539 final String[] split = valueStr.split(COMMA_DELIMITED_RE); 540 final String level = getLevelString(split, null); 541 if (level == null) { 542 warn("Level is missing for entry " + entry); 543 } else { 544 final LoggerComponentBuilder newLogger = builder.newLogger(name, level); 545 if (split.length > 1) { 546 // Add Appenders to this logger 547 final String[] sortedAppenderNames = Arrays.copyOfRange(split, 1, split.length); 548 Arrays.sort(sortedAppenderNames); 549 for (final String appenderName : sortedAppenderNames) { 550 newLogger.add(builder.newAppenderRef(appenderName)); 551 } 552 } 553 builder.add(newLogger); 554 } 555 } 556 } 557 } 558 } 559 } 560 561 private String getLog4jAppenderValue(final String appenderName, final String attributeName) { 562 return getProperty("log4j.appender." + appenderName + "." + attributeName); 563 } 564 565 private String getProperty(final String key) { 566 final String value = properties.getProperty(key); 567 final String sysValue = strSubstitutorSystem.replace(value); 568 return strSubstitutorProperties.replace(sysValue); 569 } 570 571 private String getProperty(final String key, final String defaultValue) { 572 final String value = getProperty(key); 573 return value == null ? defaultValue : value; 574 } 575 576 private String getLog4jAppenderValue(final String appenderName, final String attributeName, 577 final String defaultValue) { 578 return getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue); 579 } 580 581 private String getLog4jValue(final String key) { 582 return getProperty("log4j." + key); 583 } 584 585 private void reportWarning(final String msg) { 586 StatusLogger.getLogger().warn("Log4j 1 configuration parser: " + msg); 587 } 588 589 590 591 /* ******************************************************************* 592 * Everything from here onwards was rewritten from org.apache.log4j.config.PropertiesConfiguration, 593 * 594 */ 595 596 597 598 599 600}