Repository: logging-log4j2 Updated Branches: refs/heads/LOG4J2-1116 53a4b152e -> cd781d554
[LOG4J2-63] Support configuration from version 1.x log4j.properties. A start, with the ConsoleAppender. Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/9371266e Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/9371266e Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/9371266e Branch: refs/heads/LOG4J2-1116 Commit: 9371266e5f2afb2df8eee6721f372b0894f7c9c8 Parents: 2ccf95d Author: ggregory <ggreg...@apache.org> Authored: Wed Mar 9 00:20:15 2016 -0800 Committer: ggregory <ggreg...@apache.org> Committed: Wed Mar 9 00:20:15 2016 -0800 ---------------------------------------------------------------------- .../config/Log4j1ConfigurationFactory.java | 296 +++++++++++++++++++ .../config/Log4j1ConfigurationFactoryTest.java | 60 ++++ .../log4j-console-PatternLayout.properties | 19 ++ .../log4j-console-SimpleLayout.properties | 18 ++ 4 files changed, 393 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9371266e/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java new file mode 100644 index 0000000..470a3fe --- /dev/null +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java @@ -0,0 +1,296 @@ +/* + * 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.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.appender.ConsoleAppender.Target; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; +import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; +import org.apache.logging.log4j.status.StatusLogger; + +/** + * Experimental ConfigurationFactory for Log4j 1.2 properties files. + * <p> + * Currently supports: + * </p> + * <ul> + * <li>log4j.debug</li> + * <li>log4j.rootLogger</li> + * <li>log4j.logger</li> + * <li>log4j.appender</li> + * <li>org.apache.log4j.ConsoleAppender</li> + * <li>org.apache.log4j.PatternLayout</li> + * <ul> + * <li>Follow</li> + * <li>Target</li> + * <li>layout = org.apache.log4j.PatternLayout</li> + * <li>layout = org.apache.log4j.SimpleLayout</li> + * <li>layout.ConversionPattern</li> + * </ul> + * </ul> + */ +// TODO +// @Plugin(name = "Log4j1ConfigurationFactory", category = ConfigurationFactory.CATEGORY) +// +// Best Value? +// @Order(50) +public class Log4j1ConfigurationFactory extends ConfigurationFactory { + + private Map<String, String> buildClassToPropertyPrefixMap(final Properties properties, + final String[] sortedAppenderNames) { + final String prefix = "log4j.appender."; + final int preLength = prefix.length(); + final Map<String, String> map = new HashMap<>(sortedAppenderNames.length); + for (final Entry<Object, Object> 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); + if (Arrays.binarySearch(sortedAppenderNames, name) >= 0) { + final Object value = entry.getValue(); + if (value != null) { + map.put(name, value.toString()); + } + } + } + } + } + } + return map; + } + + private void buildConsoleAppender(final Properties properties, final String name, + final ConfigurationBuilder<BuiltConfiguration> builder) { + final AppenderComponentBuilder appenderBuilder = builder.newAppender(name, "CONSOLE"); + buildConsoleAppenderTarget(properties, name, builder, appenderBuilder); + buildConsoleAppenderLayout(properties, name, builder, appenderBuilder); + buildConsoleAppenderFollow(properties, name, builder, appenderBuilder); + builder.add(appenderBuilder); + } + + private void buildConsoleAppenderLayout(final Properties properties, final String name, + final ConfigurationBuilder<BuiltConfiguration> builder, final AppenderComponentBuilder appenderBuilder) { + final String layoutValue = getLog4jAppenderValue(properties, name, "layout", null); + if (layoutValue != null) { + final String cpValue = getLog4jAppenderValue(properties, name, "layout.ConversionPattern", null); + switch (layoutValue) { + case "org.apache.log4j.PatternLayout": { + final LayoutComponentBuilder layoutBuilder = newPatternLayout(builder, cpValue); + appenderBuilder.add(layoutBuilder); + break; + } + case "org.apache.log4j.SimpleLayout": { + final LayoutComponentBuilder layoutBuilder = newPatternLayout(builder, "%level - %m%n"); + appenderBuilder.add(layoutBuilder); + break; + } + default: + reportWarning("Unsupported value for console appender layout: " + layoutValue); + } + } + } + + private LayoutComponentBuilder newPatternLayout(final ConfigurationBuilder<BuiltConfiguration> builder, + final String pattern) { + final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout"); + if (pattern != null) { + layoutBuilder.addAttribute("pattern", pattern); + } + return layoutBuilder; + } + + private void buildConsoleAppenderTarget(final Properties properties, final String name, + final ConfigurationBuilder<BuiltConfiguration> builder, final AppenderComponentBuilder appenderBuilder) { + final String value = getLog4jAppenderValue(properties, name, "Target", "System.out"); + if (value != null) { + final Target target; + switch (value) { + case "System.out": + target = ConsoleAppender.Target.SYSTEM_OUT; + break; + case "System.err": + target = ConsoleAppender.Target.SYSTEM_ERR; + break; + default: + reportWarning("Unknow value for console Target: " + value); + target = null; + } + if (target != null) { + appenderBuilder.addAttribute("target", target); + } + } + } + + private void buildConsoleAppenderFollow(final Properties properties, final String name, + final ConfigurationBuilder<BuiltConfiguration> builder, final AppenderComponentBuilder appenderBuilder) { + final String value = getLog4jAppenderValue(properties, name, "Follow", "false"); + if (value != null) { + appenderBuilder.addAttribute("follow", Boolean.valueOf(value).booleanValue()); + } + } + + Configuration createConfiguration(final String configName, final URI configLocation, + final ConfigurationBuilder<BuiltConfiguration> builder) throws IOException { + builder.setConfigurationName(configName); + final Properties properties = load(configLocation); + if (properties == null) { + return null; + } + // DEBUG + final String debugValue = getLog4jValue(properties, "debug"); + if (Boolean.valueOf(debugValue)) { + builder.setStatusLevel(Level.DEBUG); + } + // Root + final String[] sortedAppenderNamesC = buildRootLogger(builder, getRootCategoryValue(properties)); + final String[] sortedAppenderNamesL = buildRootLogger(builder, getRootLoggerValue(properties)); + final String[] sortedAppenderNames = sortedAppenderNamesL.length > 0 ? sortedAppenderNamesL + : sortedAppenderNamesC; + // Appenders + final Map<String, String> classNameToProperty = buildClassToPropertyPrefixMap(properties, sortedAppenderNames); + for (final Entry<String, String> entry : classNameToProperty.entrySet()) { + final String appenderName = entry.getKey(); + switch (entry.getValue()) { + case "org.apache.log4j.ConsoleAppender": + buildConsoleAppender(properties, appenderName, builder); + break; + default: + reportWarning("Ignoring appender " + appenderName + + "; consider porting your configuration file to the current Log4j format."); + } + } + // Loggers + buildLoggers(properties, "log4j.category.", builder); + buildLoggers(properties, "log4j.logger.", builder); + return builder.build(); + } + + private String[] buildRootLogger(final ConfigurationBuilder<BuiltConfiguration> builder, + final String rootLoggerValue) { + if (rootLoggerValue == null) { + return new String[0]; + } + final String[] rootLoggerParts = rootLoggerValue.split("\\s*,\\s*"); + final Level rootLoggerLevel = rootLoggerParts.length > 0 ? Level.valueOf(rootLoggerParts[0]) : Level.ERROR; + builder.add(builder.newRootLogger(rootLoggerLevel)); + final String[] sortedAppenderNames = Arrays.copyOfRange(rootLoggerParts, 1, rootLoggerParts.length); + Arrays.sort(sortedAppenderNames); + return sortedAppenderNames; + } + + private void buildLoggers(final Properties properties, final String prefix, + final ConfigurationBuilder<BuiltConfiguration> builder) { + final int preLength = prefix.length(); + for (final Entry<Object, Object> 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) { + builder.add(builder.newLogger(name, Level.valueOf(value.toString()))); + } + } + } + } + + } + + @Override + public Configuration getConfiguration(final ConfigurationSource source) { + return getConfiguration(source.toString(), null); + } + + @Override + public Configuration getConfiguration(final String name, final URI configLocation) { + try { + return createConfiguration(name, configLocation, newConfigurationBuilder()); + } catch (final IOException e) { + StatusLogger.getLogger().error(e); + return null; + } + } + + private String getLog4jAppenderValue(final Properties properties, final String appenderName, + final String attributeName, final String defaultValue) { + return properties.getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue); + } + + private String getLog4jValue(final Properties properties, final String key) { + return properties.getProperty("log4j." + key); + } + + private String getRootCategoryValue(final Properties properties) { + return getLog4jValue(properties, "rootCategory"); + } + + private String getRootLoggerValue(final Properties properties) { + return getLog4jValue(properties, "rootLogger"); + } + + @Override + protected String[] getSupportedTypes() { + return new String[] { "*.properties", ".xml" }; + } + + private Properties load(final URI uri) throws IOException { + final Properties properties = toProperties(uri); + final String rootCategoryValue = getRootCategoryValue(properties); + final String rootLoggerValue = getRootLoggerValue(properties); + if (rootCategoryValue == null && rootLoggerValue == null) { + // This is not a Log4j 1 properties file. + return null; + } + return properties; + } + + private void reportWarning(final String msg) { + StatusLogger.getLogger().warn("Log4j version 1 to 2 configuration bridge: " + msg); + + } + + private Properties toProperties(final URI uri) throws IOException { + final Properties properties; + try (InputStream in = uri.toURL().openStream()) { + properties = new Properties(); + if (uri.toString().endsWith(".xml")) { + properties.loadFromXML(in); + } else { + properties.load(in); + } + } + return properties; + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9371266e/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java ---------------------------------------------------------------------- diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java new file mode 100644 index 0000000..13bcdc2 --- /dev/null +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java @@ -0,0 +1,60 @@ +/* + * 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.net.URL; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.appender.ConsoleAppender; +import org.apache.logging.log4j.core.appender.ConsoleAppender.Target; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.apache.logging.log4j.core.layout.PatternLayout; +import org.junit.Assert; +import org.junit.Test; + +public class Log4j1ConfigurationFactoryTest { + + private void testConsole(final String configResource, final String expectedPattern) throws Exception { + final URL configLocation = ClassLoader.getSystemResource(configResource); + Assert.assertNotNull(configLocation); + final Configuration configuration = new Log4j1ConfigurationFactory().getConfiguration("test", + configLocation.toURI()); + Assert.assertNotNull(configuration); + final ConsoleAppender appender = configuration.getAppender("Console"); + Assert.assertNotNull(appender); + // Can't set ImmediateFlush for a Console Appender in Log4j 2 like you can in 1.2 + Assert.assertTrue(appender.getImmediateFlush()); + Assert.assertEquals(Target.SYSTEM_ERR, appender.getTarget()); + final PatternLayout layout = (PatternLayout) appender.getLayout(); + Assert.assertEquals(expectedPattern, layout.getConversionPattern()); + // + final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo"); + Assert.assertNotNull(loggerConfig); + Assert.assertEquals(Level.DEBUG, loggerConfig.getLevel()); + } + + @Test + public void testConsolePatternLayout() throws Exception { + testConsole("config-1.2/log4j-console-PatternLayout.properties", "%d{ISO8601} [%t][%c] %-5p: %m%n"); + } + + @Test + public void testConsoleSimpleLayout() throws Exception { + testConsole("config-1.2/log4j-console-SimpleLayout.properties", "%level - %m%n"); + } +} http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9371266e/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties ---------------------------------------------------------------------- diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties new file mode 100644 index 0000000..fab7070 --- /dev/null +++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties @@ -0,0 +1,19 @@ +############################################################################### +# +# Log4J 1.2 Configuration. +# + +log4j.rootLogger=TRACE, Console + +############################################################################## +# +# The Console log +# + +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.ImmediateFlush=false +log4j.appender.Console.Target=System.err +log4j.appender.Console.layout=org.apache.log4j.PatternLayout +log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n + +log4j.logger.com.example.foo = DEBUG http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/9371266e/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties ---------------------------------------------------------------------- diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties new file mode 100644 index 0000000..5e915f8 --- /dev/null +++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties @@ -0,0 +1,18 @@ +############################################################################### +# +# Log4J 1.2 Configuration. +# + +log4j.rootLogger=TRACE, Console + +############################################################################## +# +# The Console log +# + +log4j.appender.Console=org.apache.log4j.ConsoleAppender +log4j.appender.Console.ImmediateFlush=false +log4j.appender.Console.Target=System.err +log4j.appender.Console.layout=org.apache.log4j.SimpleLayout + +log4j.logger.com.example.foo = DEBUG