diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java index f493e25bb6a..7a82831b960 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/PatternParserTest.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.pattern; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -23,6 +24,7 @@ import java.util.Calendar; import java.util.List; +import java.util.stream.Stream; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.MarkerManager; import org.apache.logging.log4j.core.LogEvent; @@ -40,6 +42,8 @@ import org.apache.logging.log4j.util.Strings; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; class PatternParserTest { @@ -98,6 +102,8 @@ void testCustomPattern() { assertNotNull(formatters); final StringMap mdc = ContextDataFactory.createContextData(); mdc.putValue("loginId", "Fred"); + // The line number of the Throwable definition + final int nextLineNumber = 107; final Throwable t = new Throwable(); final StackTraceElement[] elements = t.getStackTrace(); final Log4jLogEvent event = Log4jLogEvent.newBuilder() // @@ -116,8 +122,9 @@ void testCustomPattern() { formatter.format(event, buf); } final String str = buf.toString(); - final String expected = "INFO [PatternParserTest :101 ] - Hello, world" + Strings.LINE_SEPARATOR; - assertTrue(str.endsWith(expected), "Expected to end with: " + expected + ". Actual: " + str); + final String expected = + "INFO [PatternParserTest :" + nextLineNumber + " ] - Hello, world" + Strings.LINE_SEPARATOR; + assertThat(str).endsWith(expected); } @Test @@ -369,6 +376,39 @@ void testClosingBracketButWrongPlace() { validateConverter(formatters, 1, "Date"); } + static Stream testAlwaysWriteExceptions_ensuresPrecededByNewline() { + return Stream.of("", "%m", "%n", "%m%n"); + } + + @ParameterizedTest + @MethodSource + void testAlwaysWriteExceptions_ensuresPrecededByNewline(final String pattern) { + final List formatters = parser.parse(pattern, true, false, false); + assertNotNull(formatters); + if (pattern.endsWith("%n")) { + // Case 1: the original pattern ends with a new line, so the last converter is a ThrowablePatternConverter + assertThat(formatters).hasSizeGreaterThan(1); + final LogEventPatternConverter lastConverter = + formatters.get(formatters.size() - 1).getConverter(); + assertThat(lastConverter).isInstanceOf(ThrowablePatternConverter.class); + LogEventPatternConverter secondLastConverter = + formatters.get(formatters.size() - 2).getConverter(); + assertThat(secondLastConverter).isInstanceOf(LineSeparatorPatternConverter.class); + } else { + // Case 2: the original pattern does not end with a new line, so we add a composite converter + // that appends a new line and the exception if an exception is present. + assertThat(formatters).hasSizeGreaterThan(0); + final LogEventPatternConverter lastConverter = + formatters.get(formatters.size() - 1).getConverter(); + assertThat(lastConverter).isInstanceOf(VariablesNotEmptyReplacementConverter.class); + final List nestedFormatters = + ((VariablesNotEmptyReplacementConverter) lastConverter).formatters; + assertThat(nestedFormatters).hasSize(2); + assertThat(nestedFormatters.get(0).getConverter()).isInstanceOf(LineSeparatorPatternConverter.class); + assertThat(nestedFormatters.get(1).getConverter()).isInstanceOf(ThrowablePatternConverter.class); + } + } + @Test void testExceptionWithFilters() { final List formatters = diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java index b7cee50b3ae..80e4f008bf5 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/RootThrowablePatternConverterTest.java @@ -91,11 +91,11 @@ class StackTraceTest extends AbstractStackTraceTest { @Test @Override - void output_should_be_newline_prefixed() { + void output_should_not_be_newline_prefixed() { final String pattern = "%p" + patternPrefix; final String stackTrace = convert(pattern); final String expectedStart = String.format( - "%s%n[CIRCULAR REFERENCE: %s", LEVEL, EXCEPTION.getClass().getCanonicalName()); + "%s[CIRCULAR REFERENCE: %s", LEVEL, EXCEPTION.getClass().getCanonicalName()); assertThat(stackTrace).as("pattern=`%s`", pattern).startsWith(expectedStart); } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java index e178526fd99..24230dd23b9 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/ThrowablePatternConverterTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.Issue; /** * {@link ThrowablePatternConverter} tests. @@ -384,11 +385,12 @@ abstract static class AbstractStackTraceTest { } @Test - void output_should_be_newline_prefixed() { + @Issue("https://github.com/apache/logging-log4j2/issues/3873") + void output_should_not_be_newline_prefixed() { final String pattern = "%p" + patternPrefix; final String stackTrace = convert(pattern); final String expectedStart = - String.format("%s%n%s", LEVEL, EXCEPTION.getClass().getCanonicalName()); + String.format("%s%s", LEVEL, EXCEPTION.getClass().getCanonicalName()); assertThat(stackTrace).as("pattern=`%s`", pattern).startsWith(expectedStart); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java index 767bbda01ac..332ee535c49 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternParser.java @@ -212,7 +212,26 @@ public List parse( list.add(new PatternFormatter(pc, field)); } if (alwaysWriteExceptions && !handlesThrowable) { - final LogEventPatternConverter pc = ThrowablePatternConverter.newInstance(config, new String[0]); + // We need to guarantee that an exception is always written, + // and that it is cleanly separated from the main log line by a newline. + final LogEventPatternConverter pc; + // Look at the last converter in the existing pattern. + final PatternFormatter lastFormatter = list.isEmpty() ? null : list.get(list.size() - 1); + + if (lastFormatter == null || !(lastFormatter.getConverter() instanceof LineSeparatorPatternConverter)) { + // Case 1: The pattern does NOT already end with a newline. + // In this case, we append a composite converter `%notEmpty{%n%ex}`. + // - If no exception is present, it renders nothing (so the pattern behaves exactly as before). + // - If an exception is present, it renders a newline followed by the stack trace. + pc = VariablesNotEmptyReplacementConverter.newInstance(config, new String[] {"%n%ex"}); + } else { + // Case 2: The pattern already ends with a newline. + // In this case, we only need to add `%ex`: + // - If no exception is present, nothing changes. + // - If an exception is present, it is written immediately after the newline already in the pattern. + pc = ThrowablePatternConverter.newInstance(config, Strings.EMPTY_ARRAY); + } + // Finally, add the chosen converter to the end of the pattern. list.add(new PatternFormatter(pc, FormattingInfo.getDefault())); } return list; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableStackTraceRenderer.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableStackTraceRenderer.java index a6211147a83..b16e9b98362 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableStackTraceRenderer.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/ThrowableStackTraceRenderer.java @@ -16,8 +16,6 @@ */ package org.apache.logging.log4j.core.pattern; -import static org.apache.logging.log4j.util.Strings.LINE_SEPARATOR; - import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -55,7 +53,6 @@ public final void renderThrowable( if (maxLineCount > 0) { try { C context = createContext(throwable); - ensureNewlineSuffix(buffer); renderThrowable(buffer, throwable, context, new HashSet<>(), lineSeparator); } catch (final Exception error) { if (error != MAX_LINE_COUNT_EXCEEDED) { @@ -65,13 +62,6 @@ public final void renderThrowable( } } - private static void ensureNewlineSuffix(final StringBuilder buffer) { - final int bufferLength = buffer.length(); - if (bufferLength > 0 && buffer.charAt(bufferLength - 1) != '\n') { - buffer.append(LINE_SEPARATOR); - } - } - @SuppressWarnings("unchecked") C createContext(final Throwable throwable) { final Map metadataByThrowable = Context.Metadata.ofThrowable(throwable); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/VariablesNotEmptyReplacementConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/VariablesNotEmptyReplacementConverter.java index e5d2ba9ecfe..7fda5791d5e 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/VariablesNotEmptyReplacementConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/VariablesNotEmptyReplacementConverter.java @@ -35,7 +35,8 @@ @PerformanceSensitive("allocation") public final class VariablesNotEmptyReplacementConverter extends LogEventPatternConverter { - private final List formatters; + // package private for testing + final List formatters; /** * Constructs the converter. diff --git a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/CatchingTagTest.java b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/CatchingTagTest.java index 10ff9c1b2c5..9134c16e34a 100644 --- a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/CatchingTagTest.java +++ b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/CatchingTagTest.java @@ -57,7 +57,7 @@ public void testDoEndTag() throws Exception { this.tag.setException(new Exception("This is a test.")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Catching ERROR M-CATCHING[ EXCEPTION ] E" + LINE_SEPARATOR + "java.lang.Exception: This is a test."); + verify("Catching ERROR M-CATCHING[ EXCEPTION ] E-java.lang.Exception: This is a test."); } @Test @@ -66,8 +66,7 @@ public void testDoEndTagLevelString() throws Exception { this.tag.setException(new RuntimeException("This is another test.")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Catching INFO M-CATCHING[ EXCEPTION ] E" + LINE_SEPARATOR - + "java.lang.RuntimeException: This is another test."); + verify("Catching INFO M-CATCHING[ EXCEPTION ] E-java.lang.RuntimeException: This is another test."); } @Test @@ -76,7 +75,7 @@ public void testDoEndTagLevelObject() throws Exception { this.tag.setException(new Error("This is the last test.")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Catching WARN M-CATCHING[ EXCEPTION ] E" + LINE_SEPARATOR + "java.lang.Error: This is the last test."); + verify("Catching WARN M-CATCHING[ EXCEPTION ] E-java.lang.Error: This is the last test."); } private void verify(final String expected) { diff --git a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/EnterTagTest.java b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/EnterTagTest.java index 3cae73434cf..5f864ee1fb2 100644 --- a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/EnterTagTest.java +++ b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/EnterTagTest.java @@ -54,7 +54,7 @@ public void setUp() { @Test public void testDoEndTag() throws Exception { assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Enter TRACE M-ENTER[ FLOW ] E"); + verify("Enter TRACE M-ENTER[ FLOW ] E-"); } @Test @@ -63,7 +63,7 @@ public void testDoEndTagAttributes() throws Exception { this.tag.setDynamicAttribute(null, null, 5792); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Enter params(log4j-test1.xml, 5792) TRACE M-ENTER[ FLOW ] E"); + verify("Enter params(log4j-test1.xml, 5792) TRACE M-ENTER[ FLOW ] E-"); } private void verify(final String expected) { diff --git a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/ExitTagTest.java b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/ExitTagTest.java index 1e2fa7d79fe..3f387b15cfb 100644 --- a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/ExitTagTest.java +++ b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/ExitTagTest.java @@ -53,7 +53,7 @@ public void setUp() { @Test public void testDoEndTag() throws Exception { assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Exit TRACE M-EXIT[ FLOW ] E"); + verify("Exit TRACE M-EXIT[ FLOW ] E-"); } @Test @@ -61,7 +61,7 @@ public void testDoEndTagResult01() throws Exception { this.tag.setResult(CONFIG); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Exit with(log4j-test1.xml) TRACE M-EXIT[ FLOW ] E"); + verify("Exit with(log4j-test1.xml) TRACE M-EXIT[ FLOW ] E-"); } @Test @@ -69,7 +69,7 @@ public void testDoEndTagResult02() throws Exception { this.tag.setResult(5792); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Exit with(5792) TRACE M-EXIT[ FLOW ] E"); + verify("Exit with(5792) TRACE M-EXIT[ FLOW ] E-"); } private void verify(final String expected) { diff --git a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/LoggingMessageTagSupportTest.java b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/LoggingMessageTagSupportTest.java index 5fb2b419b4c..ad9ab5d9ef7 100644 --- a/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/LoggingMessageTagSupportTest.java +++ b/log4j-taglib/src/test/java/org/apache/logging/log4j/taglib/LoggingMessageTagSupportTest.java @@ -118,7 +118,7 @@ public void testDoEndTagStringMessageNoMarkerNoException() throws Exception { this.tag.setMessage("Hello message for testDoEndTagStringMessageNoMarkerNoException"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Hello message for testDoEndTagStringMessageNoMarkerNoException WARN M- E"); + verify("Hello message for testDoEndTagStringMessageNoMarkerNoException WARN M- E-"); } @Test @@ -129,7 +129,7 @@ public void testDoEndTagStringMessageMarkerNoException() throws Exception { this.tag.setMessage("Goodbye message for testDoEndTagStringMessageMarkerNoException"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Goodbye message for testDoEndTagStringMessageMarkerNoException INFO M-E01 E"); + verify("Goodbye message for testDoEndTagStringMessageMarkerNoException INFO M-E01 E-"); } @Test @@ -140,8 +140,9 @@ public void testDoEndTagStringMessageNoMarkerException() throws Exception { this.tag.setMessage("Another message for testDoEndTagStringMessageNoMarkerException"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Another message for testDoEndTagStringMessageNoMarkerException ERROR M- E" + LINE_SEPARATOR - + "java.lang.Exception: This is a test" + LINE_SEPARATOR); + verify( + "Another message for testDoEndTagStringMessageNoMarkerException ERROR M- E-java.lang.Exception: This is a test" + + LINE_SEPARATOR); } @Test @@ -153,8 +154,9 @@ public void testDoEndTagStringMessageMarkerException() throws Exception { this.tag.setMessage("Final message for testDoEndTagStringMessageMarkerException"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Final message for testDoEndTagStringMessageMarkerException TRACE M-F02 E" + LINE_SEPARATOR - + "java.lang.RuntimeException: This is another test" + Strings.LINE_SEPARATOR); + verify( + "Final message for testDoEndTagStringMessageMarkerException TRACE M-F02 E-java.lang.RuntimeException: This is another test" + + Strings.LINE_SEPARATOR); } @Test @@ -166,7 +168,7 @@ public void testDoEndTagStringWithParameters() throws Exception { this.tag.setMessage("Test message with [{}] parameter of [{}]"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Test message with [A] parameter of [HOURS] FATAL M- E"); + verify("Test message with [A] parameter of [HOURS] FATAL M- E-"); } @Test @@ -180,8 +182,8 @@ public void testDoEndTagStringWithParametersMarkerAndException() throws Exceptio this.tag.setMessage("Final message with [{}] parameter of [{}]"); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Final message with [Z] parameter of [SECONDS] DEBUG M-N03 E" + LINE_SEPARATOR - + "java.lang.Error: This is the last test" + LINE_SEPARATOR); + verify("Final message with [Z] parameter of [SECONDS] DEBUG M-N03 E-java.lang.Error: This is the last test" + + LINE_SEPARATOR); } @Test @@ -192,7 +194,7 @@ public void testDoEndTagMessageNoMarkerNoException() throws Exception { logger.getMessageFactory().newMessage("First message for testDoEndTagMessageNoMarkerNoException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("First message for testDoEndTagMessageNoMarkerNoException INFO M- E"); + verify("First message for testDoEndTagMessageNoMarkerNoException INFO M- E-"); } @Test @@ -204,7 +206,7 @@ public void testDoEndTagMessageMarkerNoException() throws Exception { logger.getMessageFactory().newMessage("Another message for testDoEndTagMessageMarkerNoException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Another message for testDoEndTagMessageMarkerNoException WARN M-E01 E"); + verify("Another message for testDoEndTagMessageMarkerNoException WARN M-E01 E-"); } @Test @@ -216,8 +218,8 @@ public void testDoEndTagMessageNoMarkerException() throws Exception { logger.getMessageFactory().newMessage("Third message for testDoEndTagMessageNoMarkerException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Third message for testDoEndTagMessageNoMarkerException TRACE M- E" + LINE_SEPARATOR - + "java.lang.Exception: This is a test" + LINE_SEPARATOR); + verify("Third message for testDoEndTagMessageNoMarkerException TRACE M- E-java.lang.Exception: This is a test" + + LINE_SEPARATOR); } @Test @@ -230,8 +232,9 @@ public void testDoEndTagMessageMarkerException() throws Exception { logger.getMessageFactory().newMessage("Final message for testDoEndTagMessageMarkerException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Final message for testDoEndTagMessageMarkerException ERROR M-F02 E" + LINE_SEPARATOR - + "java.lang.RuntimeException: " + "This is another test" + LINE_SEPARATOR); + verify( + "Final message for testDoEndTagMessageMarkerException ERROR M-F02 E-java.lang.RuntimeException: This is another test" + + LINE_SEPARATOR); } @Test @@ -241,7 +244,7 @@ public void testDoEndTagObjectNoMarkerNoException() throws Exception { this.tag.setMessage(new MyMessage("First message for testDoEndTagObjectNoMarkerNoException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("First message for testDoEndTagObjectNoMarkerNoException INFO M- E"); + verify("First message for testDoEndTagObjectNoMarkerNoException INFO M- E-"); } @Test @@ -252,7 +255,7 @@ public void testDoEndTagObjectMarkerNoException() throws Exception { this.tag.setMessage(new MyMessage("Another message for testDoEndTagObjectMarkerNoException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Another message for testDoEndTagObjectMarkerNoException WARN M-E01 E"); + verify("Another message for testDoEndTagObjectMarkerNoException WARN M-E01 E-"); } @Test @@ -263,8 +266,8 @@ public void testDoEndTagObjectNoMarkerException() throws Exception { this.tag.setMessage(new MyMessage("Third message for testDoEndTagObjectNoMarkerException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Third message for testDoEndTagObjectNoMarkerException TRACE M- E" + LINE_SEPARATOR - + "java.lang.Exception: This is a test" + LINE_SEPARATOR); + verify("Third message for testDoEndTagObjectNoMarkerException TRACE M- E-java.lang.Exception: This is a test" + + LINE_SEPARATOR); } @Test @@ -276,8 +279,9 @@ public void testDoEndTagObjectMarkerException() throws Exception { this.tag.setMessage(new MyMessage("Final message for testDoEndTagObjectMarkerException")); assertEquals(Tag.EVAL_PAGE, this.tag.doEndTag(), "The return value is not correct."); - verify("Final message for testDoEndTagObjectMarkerException ERROR M-F02 E" + LINE_SEPARATOR - + "java.lang.RuntimeException: " + "This is another test" + LINE_SEPARATOR); + verify( + "Final message for testDoEndTagObjectMarkerException ERROR M-F02 E-java.lang.RuntimeException: This is another test" + + LINE_SEPARATOR); } private void verify(final String expected) { diff --git a/log4j-taglib/src/test/resources/log4j-test1.xml b/log4j-taglib/src/test/resources/log4j-test1.xml index 31de3720339..6cd3a4eb992 100644 --- a/log4j-taglib/src/test/resources/log4j-test1.xml +++ b/log4j-taglib/src/test/resources/log4j-test1.xml @@ -34,7 +34,7 @@ - + diff --git a/src/changelog/.2.x.x/3873_throwable_converter_new_line.xml b/src/changelog/.2.x.x/3873_throwable_converter_new_line.xml new file mode 100644 index 00000000000..30011263e52 --- /dev/null +++ b/src/changelog/.2.x.x/3873_throwable_converter_new_line.xml @@ -0,0 +1,13 @@ + + + + + + Fix Pattern Layout exception stack trace converters to no longer prepend newlines based on context + +