All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.config.Log4j1ConfigurationParser Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta2
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.log4j.config;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.TreeMap;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.appender.NullAppender;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
import org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;

/**
 * Experimental parser for Log4j 1.2 properties configuration files.
 *
 * This class is not thread-safe.
 * 
 * 

* From the Log4j 1.2 Javadocs: *

*

* All option values admit variable substitution. The syntax of variable substitution is similar to that of Unix shells. The string between * an opening "${" and closing "}" is interpreted as a key. The value of the substituted variable can be defined as a system property or in * the configuration file itself. The value of the key is first searched in the system properties, and if not found there, it is then * searched in the configuration file being parsed. The corresponding value replaces the ${variableName} sequence. For example, if java.home * system property is set to /home/xyz, then every occurrence of the sequence ${java.home} will be interpreted as /home/xyz. *

*/ public class Log4j1ConfigurationParser { private static final String COMMA_DELIMITED_RE = "\\s*,\\s*"; private static final String ROOTLOGGER = "rootLogger"; private static final String ROOTCATEGORY = "rootCategory"; private static final String TRUE = "true"; private static final String FALSE = "false"; private final Properties properties = new Properties(); private StrSubstitutor strSubstitutorProperties; private StrSubstitutor strSubstitutorSystem; private final ConfigurationBuilder builder = ConfigurationBuilderFactory .newConfigurationBuilder(); /** * Parses a Log4j 1.2 properties configuration file in ISO 8859-1 encoding into a ConfigurationBuilder. * * @param input * InputStream to read from is assumed to be ISO 8859-1, and will not be closed. * @return the populated ConfigurationBuilder, never {@literal null} * @throws IOException * if unable to read the input * @throws ConfigurationException * if the input does not contain a valid configuration */ public ConfigurationBuilder buildConfigurationBuilder(final InputStream input) throws IOException { try { properties.load(input); strSubstitutorProperties = new ConfigurationStrSubstitutor(properties); strSubstitutorSystem = new ConfigurationStrSubstitutor(System.getProperties()); final String rootCategoryValue = getLog4jValue(ROOTCATEGORY); final String rootLoggerValue = getLog4jValue(ROOTLOGGER); if (rootCategoryValue == null && rootLoggerValue == null) { // This is not a Log4j 1 properties configuration file. warn("Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input); // throw new ConfigurationException( // "Missing " + ROOTCATEGORY + " or " + ROOTLOGGER + " in " + input); } builder.setConfigurationName("Log4j1"); // DEBUG final String debugValue = getLog4jValue("debug"); if (Boolean.valueOf(debugValue)) { builder.setStatusLevel(Level.DEBUG); } // Root buildRootLogger(getLog4jValue(ROOTCATEGORY)); buildRootLogger(getLog4jValue(ROOTLOGGER)); // Appenders final Map appenderNameToClassName = buildClassToPropertyPrefixMap(); for (final Map.Entry entry : appenderNameToClassName.entrySet()) { final String appenderName = entry.getKey(); final String appenderClass = entry.getValue(); buildAppender(appenderName, appenderClass); } // Loggers buildLoggers("log4j.category."); buildLoggers("log4j.logger."); buildProperties(); return builder; } catch (final IllegalArgumentException e) { throw new ConfigurationException(e); } } private void buildProperties() { for (final Map.Entry entry : new TreeMap<>(properties).entrySet()) { final String key = entry.getKey().toString(); if (!key.startsWith("log4j.") && !key.equals(ROOTCATEGORY) && !key.equals(ROOTLOGGER)) { builder.addProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY)); } } } private void warn(final String string) { System.err.println(string); } private Map buildClassToPropertyPrefixMap() { final String prefix = "log4j.appender."; final int preLength = prefix.length(); final Map map = new HashMap<>(); for (final Map.Entry entry : properties.entrySet()) { final Object keyObj = entry.getKey(); if (keyObj != null) { final String key = keyObj.toString(); if (key.startsWith(prefix)) { if (key.indexOf('.', preLength) < 0) { final String name = key.substring(preLength); final Object value = entry.getValue(); if (value != null) { map.put(name, value.toString()); } } } } } return map; } private void buildAppender(final String appenderName, final String appenderClass) { switch (appenderClass) { case "org.apache.log4j.ConsoleAppender": buildConsoleAppender(appenderName); break; case "org.apache.log4j.FileAppender": buildFileAppender(appenderName); break; case "org.apache.log4j.DailyRollingFileAppender": buildDailyRollingFileAppender(appenderName); break; case "org.apache.log4j.RollingFileAppender": buildRollingFileAppender(appenderName); break; case "org.apache.log4j.varia.NullAppender": buildNullAppender(appenderName); break; default: reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName); } } private void buildConsoleAppender(final String appenderName) { final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, ConsoleAppender.PLUGIN_NAME); final String targetValue = getLog4jAppenderValue(appenderName, "Target", "System.out"); if (targetValue != null) { final ConsoleAppender.Target target; switch (targetValue) { case "System.out": target = ConsoleAppender.Target.SYSTEM_OUT; break; case "System.err": target = ConsoleAppender.Target.SYSTEM_ERR; break; default: reportWarning("Unknown value for console Target: " + targetValue); target = null; } if (target != null) { appenderBuilder.addAttribute("target", target); } } buildAttribute(appenderName, appenderBuilder, "Follow", "follow"); if (FALSE.equalsIgnoreCase(getLog4jAppenderValue(appenderName, "ImmediateFlush"))) { reportWarning("ImmediateFlush=false is not supported on Console appender"); } buildAppenderLayout(appenderName, appenderBuilder); builder.add(appenderBuilder); } private void buildFileAppender(final String appenderName) { final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, FileAppender.PLUGIN_NAME); buildFileAppender(appenderName, appenderBuilder); builder.add(appenderBuilder); } private void buildFileAppender(final String appenderName, final AppenderComponentBuilder appenderBuilder) { buildMandatoryAttribute(appenderName, appenderBuilder, "File", "fileName"); buildAttribute(appenderName, appenderBuilder, "Append", "append"); buildAttribute(appenderName, appenderBuilder, "BufferedIO", "bufferedIo"); buildAttribute(appenderName, appenderBuilder, "BufferSize", "bufferSize"); buildAttribute(appenderName, appenderBuilder, "ImmediateFlush", "immediateFlush"); buildAppenderLayout(appenderName, appenderBuilder); } private void buildDailyRollingFileAppender(final String appenderName) { final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, RollingFileAppender.PLUGIN_NAME); buildFileAppender(appenderName, appenderBuilder); final String fileName = getLog4jAppenderValue(appenderName, "File"); final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", fileName + "'.'yyyy-MM-dd"); appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}"); final ComponentBuilder triggeringPolicy = builder.newComponent("Policies") .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true)); appenderBuilder.addComponent(triggeringPolicy); appenderBuilder .addComponent(builder.newComponent("DefaultRolloverStrategy").addAttribute("max", Integer.MAX_VALUE)); builder.add(appenderBuilder); } private void buildRollingFileAppender(final String appenderName) { final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, RollingFileAppender.PLUGIN_NAME); buildFileAppender(appenderName, appenderBuilder); final String fileName = getLog4jAppenderValue(appenderName, "File"); appenderBuilder.addAttribute("filePattern", fileName + ".%i"); final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760"); final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1"); final ComponentBuilder triggeringPolicy = builder.newComponent("Policies").addComponent( builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString)); appenderBuilder.addComponent(triggeringPolicy); appenderBuilder.addComponent( builder.newComponent("DefaultRolloverStrategy").addAttribute("max", maxBackupIndexString)); builder.add(appenderBuilder); } private void buildAttribute(final String componentName, final ComponentBuilder componentBuilder, final String sourceAttributeName, final String targetAttributeName) { final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); if (attributeValue != null) { componentBuilder.addAttribute(targetAttributeName, attributeValue); } } private void buildAttributeWithDefault(final String componentName, final ComponentBuilder componentBuilder, final String sourceAttributeName, final String targetAttributeName, final String defaultValue) { final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName, defaultValue); componentBuilder.addAttribute(targetAttributeName, attributeValue); } private void buildMandatoryAttribute(final String componentName, final ComponentBuilder componentBuilder, final String sourceAttributeName, final String targetAttributeName) { final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); if (attributeValue != null) { componentBuilder.addAttribute(targetAttributeName, attributeValue); } else { reportWarning("Missing " + sourceAttributeName + " for " + componentName); } } private void buildNullAppender(final String appenderName) { final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName, NullAppender.PLUGIN_NAME); builder.add(appenderBuilder); } private void buildAppenderLayout(final String name, final AppenderComponentBuilder appenderBuilder) { final String layoutClass = getLog4jAppenderValue(name, "layout", null); if (layoutClass != null) { switch (layoutClass) { case "org.apache.log4j.PatternLayout": case "org.apache.log4j.EnhancedPatternLayout": { final String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null) // Log4j 2's %x (NDC) is not compatible with Log4j 1's // %x // Log4j 1: "foo bar baz" // Log4j 2: "[foo, bar, baz]" // Use %ndc to get the Log4j 1 format .replace("%x", "%ndc") // Log4j 2's %X (MDC) is not compatible with Log4j 1's // %X // Log4j 1: "{{foo,bar}{hoo,boo}}" // Log4j 2: "{foo=bar,hoo=boo}" // Use %properties to get the Log4j 1 format .replace("%X", "%properties"); appenderBuilder.add(newPatternLayout(pattern)); break; } case "org.apache.log4j.SimpleLayout": { appenderBuilder.add(newPatternLayout("%level - %m%n")); break; } case "org.apache.log4j.TTCCLayout": { String pattern = "%r "; if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) { pattern += "[%t] "; } pattern += "%p "; if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) { pattern += "%c "; } if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) { pattern += "%notEmpty{%ndc }"; } pattern += "- %m%n"; appenderBuilder.add(newPatternLayout(pattern)); break; } case "org.apache.log4j.HTMLLayout": { final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout"); htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages")); htmlLayout.addAttribute("locationInfo", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); appenderBuilder.add(htmlLayout); break; } case "org.apache.log4j.xml.XMLLayout": { final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout"); xmlLayout.addAttribute("locationInfo", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); xmlLayout.addAttribute("properties", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE))); appenderBuilder.add(xmlLayout); break; } default: reportWarning("Unknown layout class: " + layoutClass); } } } private LayoutComponentBuilder newPatternLayout(final String pattern) { final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout"); if (pattern != null) { layoutBuilder.addAttribute("pattern", pattern); } return layoutBuilder; } private void buildRootLogger(final String rootLoggerValue) { if (rootLoggerValue == null) { return; } final String[] rootLoggerParts = rootLoggerValue.split(COMMA_DELIMITED_RE); final String rootLoggerLevel = getLevelString(rootLoggerParts, Level.ERROR.name()); final RootLoggerComponentBuilder loggerBuilder = builder.newRootLogger(rootLoggerLevel); // final String[] sortedAppenderNames = Arrays.copyOfRange(rootLoggerParts, 1, rootLoggerParts.length); Arrays.sort(sortedAppenderNames); for (final String appender : sortedAppenderNames) { loggerBuilder.add(builder.newAppenderRef(appender)); } builder.add(loggerBuilder); } private String getLevelString(final String[] loggerParts, final String defaultLevel) { return loggerParts.length > 0 ? loggerParts[0] : defaultLevel; } private void buildLoggers(final String prefix) { final int preLength = prefix.length(); for (final Map.Entry entry : properties.entrySet()) { final Object keyObj = entry.getKey(); if (keyObj != null) { final String key = keyObj.toString(); if (key.startsWith(prefix)) { final String name = key.substring(preLength); final Object value = entry.getValue(); if (value != null) { // a Level may be followed by a list of Appender refs. final String valueStr = value.toString(); final String[] split = valueStr.split(COMMA_DELIMITED_RE); final String level = getLevelString(split, null); if (level == null) { warn("Level is missing for entry " + entry); } else { final LoggerComponentBuilder newLogger = builder.newLogger(name, level); if (split.length > 1) { // Add Appenders to this logger final String[] sortedAppenderNames = Arrays.copyOfRange(split, 1, split.length); Arrays.sort(sortedAppenderNames); for (final String appenderName : sortedAppenderNames) { newLogger.add(builder.newAppenderRef(appenderName)); } } builder.add(newLogger); } } } } } } private String getLog4jAppenderValue(final String appenderName, final String attributeName) { return getProperty("log4j.appender." + appenderName + "." + attributeName); } private String getProperty(final String key) { final String value = properties.getProperty(key); final String sysValue = strSubstitutorSystem.replace(value); return strSubstitutorProperties.replace(sysValue); } private String getProperty(final String key, final String defaultValue) { final String value = getProperty(key); return value == null ? defaultValue : value; } private String getLog4jAppenderValue(final String appenderName, final String attributeName, final String defaultValue) { return getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue); } private String getLog4jValue(final String key) { return getProperty("log4j." + key); } private void reportWarning(final String msg) { StatusLogger.getLogger().warn("Log4j 1 configuration parser: " + msg); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy