You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2022/03/31 19:43:38 UTC

[logging-log4j2] 09/15: [LOG4J2-3419] Add a Log4j 1.x level name pattern converter

This is an automated email from the ASF dual-hosted git repository.

pkarwasz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 4c63bbf7299b404a308c61167db83b0c32d9d322
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Mon Mar 14 21:52:10 2022 +0100

    [LOG4J2-3419] Add a Log4j 1.x level name pattern converter
    
    When custom levels are in play the names of Log4j 1.x custom levels
    (which are not unique) and those of Log4j 2.x custom levels (which must
    be unique) do not match.
    
    There is a need therefore for a pattern converter specific to Log4j 1.x.
    
    Conflicts:
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
    	log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
---
 log4j-1.2-api/pom.xml                              |  4 ++
 .../builders/layout/PatternLayoutBuilder.java      |  8 ++--
 .../log4j/builders/layout/SimpleLayoutBuilder.java |  4 +-
 .../log4j/config/Log4j1ConfigurationParser.java    | 30 +++++++------
 .../log4j/pattern/Log4j1LevelPatternConverter.java | 48 ++++++++++++++++++++
 .../builders/layout/PatternLayoutBuilderTest.java  | 51 ++++++++++++++++++++++
 .../config/AbstractLog4j1ConfigurationTest.java    | 10 +++--
 .../config/Log4j1ConfigurationFactoryTest.java     |  2 +-
 .../log4j/config/PropertiesConfigurationTest.java  |  2 +-
 .../pattern/Log4j1LevelPatternConverterTest.java   | 46 +++++++++++++++++++
 10 files changed, 180 insertions(+), 25 deletions(-)

diff --git a/log4j-1.2-api/pom.xml b/log4j-1.2-api/pom.xml
index c3bf53e..27087ba 100644
--- a/log4j-1.2-api/pom.xml
+++ b/log4j-1.2-api/pom.xml
@@ -47,6 +47,10 @@
       <artifactId>junit-jupiter-params</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <scope>test</scope>
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
index 1d1dd2c..8d87942 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -79,26 +79,28 @@ public class PatternLayoutBuilder extends AbstractBuilder<Layout> implements Lay
         return createLayout(pattern, config);
     }
 
-    private Layout createLayout(String pattern, final Log4j1Configuration config) {
+    Layout createLayout(String pattern, final Log4j1Configuration config) {
         if (pattern == null) {
             LOGGER.info("No pattern provided for pattern layout, using default pattern");
             pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
         }
         return LayoutWrapper.adapt(PatternLayout.newBuilder()
                 .setPattern(pattern
+                        // Log4j 2 and Log4j 1 level names differ for custom levels
+                        .replaceAll("%([-\\.\\d]*)p", "%$1v1Level")
                         // 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")
+                        .replaceAll("%([-\\.\\d]*)x", "%$1ndc")
 
                         // 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"))
+                        .replaceAll("%([-\\.\\d]*)X", "%$1properties"))
                 .setConfiguration(config)
                 .build());
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
index 00d7c2c..38492e3 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -35,7 +35,7 @@ public class SimpleLayoutBuilder implements LayoutBuilder {
     @Override
     public Layout parse(Element layoutElement, XmlConfiguration config) {
         return new LayoutWrapper(PatternLayout.newBuilder()
-                .setPattern("%level - %m%n")
+                .setPattern("%v1Level - %m%n")
                 .setConfiguration(config)
                 .build());
     }
@@ -43,7 +43,7 @@ public class SimpleLayoutBuilder implements LayoutBuilder {
     @Override
     public Layout parse(PropertiesConfiguration config) {
         return new LayoutWrapper(PatternLayout.newBuilder()
-                .setPattern("%level - %m%n")
+                .setPattern("%v1Level - %m%n")
                 .setConfiguration(config)
                 .build());
     }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
index 985594c..33a0ba2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
@@ -294,19 +294,21 @@ public class Log4j1ConfigurationParser {
                 String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null);
                 if (pattern != null) {
                     pattern = pattern
-                        // 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");
+                            // Log4j 2 and Log4j 1 level names differ for custom levels
+                            .replaceAll("%([-\\.\\d]*)p", "%$1v1Level")
+                            // 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
+                            .replaceAll("%([-\\.\\d]*)x", "%$1ndc")
+
+                            // 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
+                            .replaceAll("%([-\\.\\d]*)X", "%$1properties");
                 } else {
                     pattern = "%m%n";
                 }
@@ -314,7 +316,7 @@ public class Log4j1ConfigurationParser {
                 break;
             }
             case "org.apache.log4j.SimpleLayout": {
-                appenderBuilder.add(newPatternLayout("%level - %m%n"));
+                appenderBuilder.add(newPatternLayout("%v1Level - %m%n"));
                 break;
             }
             case "org.apache.log4j.TTCCLayout": {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
new file mode 100644
index 0000000..a7d474a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.pattern;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+import org.apache.logging.log4j.plugins.Plugin;
+
+/**
+ * Outputs the Log4j 1.x level name.
+ */
+@Plugin(name = "Log4j1LevelPatternConverter", category = PatternConverter.CATEGORY)
+@ConverterKeys({ "v1Level" })
+public class Log4j1LevelPatternConverter extends LogEventPatternConverter {
+
+    private static final Log4j1LevelPatternConverter INSTANCE = new Log4j1LevelPatternConverter();
+
+    public static Log4j1LevelPatternConverter newInstance(final String[] options) {
+        return INSTANCE;
+    }
+
+    private Log4j1LevelPatternConverter() {
+        super("Log4j1Level", "v1Level");
+    }
+
+    @Override
+    public void format(LogEvent event, StringBuilder toAppendTo) {
+        toAppendTo.append(OptionConverter.convertLevel(event.getLevel()).toString());
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
new file mode 100644
index 0000000..9a22389
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.builders.layout;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class PatternLayoutBuilderTest {
+
+    static Stream<Arguments> patterns() {
+        return Arrays.asList(
+                    Arguments.of("%p", "%v1Level"),
+                    Arguments.of("%100p", "%100v1Level"),
+                    Arguments.of("%-100p", "%-100v1Level"),
+                    Arguments.of("%x", "%ndc"),
+                    Arguments.of("%X", "%properties"),
+                    Arguments.of("%.20x", "%.20ndc"))
+                .stream();
+    }
+
+    @ParameterizedTest
+    @MethodSource("patterns")
+    public void testLevelPatternReplacement(final String v1Pattern, final String v2Pattern) {
+        final PatternLayoutBuilder builder = new PatternLayoutBuilder();
+        final PatternLayout layout = (PatternLayout) LayoutAdapter.adapt(builder.createLayout(v1Pattern, null));
+        assertEquals(v2Pattern, layout.getConversionPattern());
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
index 6ba1373..f04ec2b 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -271,7 +271,8 @@ public abstract class AbstractLog4j1ConfigurationTest {
 
     public void testConsoleEnhancedPatternLayout() throws Exception {
         final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-EnhancedPatternLayout");
-        assertEquals("%d{ISO8601} [%t][%c] %-5p %properties %ndc: %m%n", layout.getConversionPattern());
+        // %p, %X and %x converted to their Log4j 1.x bridge equivalent
+        assertEquals("%d{ISO8601} [%t][%c] %-5v1Level %properties %ndc: %m%n", layout.getConversionPattern());
     }
 
     public void testConsoleHtmlLayout() throws Exception {
@@ -282,17 +283,18 @@ public abstract class AbstractLog4j1ConfigurationTest {
 
     public void testConsolePatternLayout() throws Exception {
         final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout");
-        assertEquals("%d{ISO8601} [%t][%c] %-5p: %m%n", layout.getConversionPattern());
+        // %p converted to its Log4j 1.x bridge equivalent
+        assertEquals("%d{ISO8601} [%t][%c] %-5v1Level: %m%n", layout.getConversionPattern());
     }
 
     public void testConsoleSimpleLayout() throws Exception {
         final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout");
-        assertEquals("%level - %m%n", layout.getConversionPattern());
+        assertEquals("%v1Level - %m%n", layout.getConversionPattern());
     }
 
     public void testFileSimpleLayout() throws Exception {
         final PatternLayout layout = (PatternLayout) testFile("config-1.2/log4j-file-SimpleLayout");
-        assertEquals("%level - %m%n", layout.getConversionPattern());
+        assertEquals("%v1Level - %m%n", layout.getConversionPattern());
     }
 
     public void testNullAppender() throws Exception {
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
index 5b591a6..4d7e315 100644
--- 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
@@ -168,7 +168,7 @@ public class Log4j1ConfigurationFactoryTest extends AbstractLog4j1ConfigurationT
             assertTrue(appender instanceof ConsoleAppender);
             final Layout<? extends Serializable> layout = appender.getLayout();
             assertTrue(layout instanceof PatternLayout);
-            assertEquals("%level - %m%n", ((PatternLayout)layout).getConversionPattern());
+            assertEquals("%v1Level - %m%n", ((PatternLayout)layout).getConversionPattern());
             // No filter support
             config.start();
             config.stop();
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
index d00cad4..bf4705e 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -301,7 +301,7 @@ public class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest
             assertTrue(appender instanceof ConsoleAppender);
             final Layout<? extends Serializable> layout = appender.getLayout();
             assertTrue(layout instanceof PatternLayout);
-            assertEquals("%level - %m%n", ((PatternLayout)layout).getConversionPattern());
+            assertEquals("%v1Level - %m%n", ((PatternLayout)layout).getConversionPattern());
             final Filter filter = ((Filterable) appender).getFilter();
             assertTrue(filter instanceof DenyAllFilter);
             config.start();
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
new file mode 100644
index 0000000..11150b0
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.pattern;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class Log4j1LevelPatternConverterTest {
+
+    /**
+     * Tests if the converter returns the Log4j 1.x {@code toString()} value of
+     * custom Log4j 1.x levels.
+     * 
+     * @param level a Log4j 1.x level
+     */
+    @ParameterizedTest
+    @MethodSource("org.apache.log4j.helpers.UtilLoggingLevel#getAllPossibleLevels")
+    public void testUtilLoggingLevels(final Level level) {
+        final Log4j1LevelPatternConverter converter = Log4j1LevelPatternConverter.newInstance(null);
+        final LogEvent logEvent = mock(LogEvent.class);
+        when(logEvent.getLevel()).thenReturn(level.getVersion2Level());
+        final StringBuilder result = new StringBuilder();
+        converter.format(logEvent, result);
+        assertEquals(level.toString(), result.toString());
+    }
+}