Commit 291e0361 authored by Piotr P. Karwasz's avatar Piotr P. Karwasz Committed by ppkarwasz
Browse files

[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.
parent 65496a56
Showing with 180 additions and 25 deletions
+180 -25
......@@ -57,6 +57,10 @@
<groupId>org.junit.jupiter</groupId>
<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>
......
......@@ -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()
.withPattern(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"))
.withConfiguration(config)
.build());
}
......
......@@ -35,7 +35,7 @@ public class SimpleLayoutBuilder implements LayoutBuilder {
@Override
public Layout parse(Element layoutElement, XmlConfiguration config) {
return new LayoutWrapper(PatternLayout.newBuilder()
.withPattern("%level - %m%n")
.withPattern("%v1Level - %m%n")
.withConfiguration(config)
.build());
}
......@@ -43,7 +43,7 @@ public class SimpleLayoutBuilder implements LayoutBuilder {
@Override
public Layout parse(PropertiesConfiguration config) {
return new LayoutWrapper(PatternLayout.newBuilder()
.withPattern("%level - %m%n")
.withPattern("%v1Level - %m%n")
.withConfiguration(config)
.build());
}
......
......@@ -297,19 +297,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";
}
......@@ -317,7 +319,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": {
......
/*
* 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.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
/**
* 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());
}
}
/*
* 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());
}
}
......@@ -276,7 +276,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 {
......@@ -287,17 +288,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 {
......
......@@ -167,7 +167,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();
......
......@@ -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();
......
/*
* 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());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment