This is an automated email from the ASF dual-hosted git repository.
pkarwasz pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/2.x by this push:
new c41feeddad fix: make `%ex` behavior context-independent (#3919)
c41feeddad is described below
commit c41feeddad05f96fc73d3c0fb6a9da6763431aec
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Fri Sep 12 13:00:06 2025 +0200
fix: make `%ex` behavior context-independent (#3919)
Historically, throwable pattern converters (`%ex`, `%xEx`, etc.) behaved in
a context-sensitive way:
* If the **preceding formatter’s expansion** did not end with whitespace,
the converter automatically inserted a space before rendering the exception.
In version `2.25.0`, this was changed to insert a **newline** instead of a
space, but the behavior was still dependent on surrounding context.
#### What this change does
This PR removes the context-dependent behavior altogether and makes `%ex`
expansion fully predictable, while remaining backward-compatible:
* When `%ex` is **added implicitly** because `alwaysWriteExceptions=true`:
* If the pattern already ends with `%n`, a plain `%ex` is appended.
* Otherwise, `%notEmpty{%n%ex}` is appended.
This ensures exceptions are always clearly separated from the main log
message by a newline, without adding extra characters when no exception is
present.
* When `%ex` is **explicitly included** in the pattern by the user:
* Its expansion is rendered exactly as written in the pattern.
* It will **never** prepend a newline on its own.
#### Why
* Eliminates confusing context-sensitive behavior.
* Makes output consistent and predictable.
* Preserves legacy expectations by only modifying implicitly added `%ex`.
Closes #3873
Co-authored-by: Volkan Yazıcı <[email protected]>
---
.../log4j/core/pattern/PatternParserTest.java | 44 ++++++++++++++++++++-
.../pattern/RootThrowablePatternConverterTest.java | 4 +-
.../pattern/ThrowablePatternConverterTest.java | 6 ++-
.../logging/log4j/core/pattern/PatternParser.java | 21 +++++++++-
.../core/pattern/ThrowableStackTraceRenderer.java | 10 -----
.../VariablesNotEmptyReplacementConverter.java | 3 +-
.../logging/log4j/taglib/CatchingTagTest.java | 7 ++--
.../apache/logging/log4j/taglib/EnterTagTest.java | 4 +-
.../apache/logging/log4j/taglib/ExitTagTest.java | 6 +--
.../log4j/taglib/LoggingMessageTagSupportTest.java | 46 ++++++++++++----------
log4j-taglib/src/test/resources/log4j-test1.xml | 2 +-
.../.2.x.x/3873_throwable_converter_new_line.xml | 13 ++++++
12 files changed, 117 insertions(+), 49 deletions(-)
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 f493e25bb6..7a82831b96 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 static org.junit.jupiter.api.Assertions.assertTrue;
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.StringMap;
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 @@ class PatternParserTest {
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 @@ class PatternParserTest {
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 @@ class PatternParserTest {
validateConverter(formatters, 1, "Date");
}
+ static Stream<String> testAlwaysWriteExceptions_ensuresPrecededByNewline()
{
+ return Stream.of("", "%m", "%n", "%m%n");
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testAlwaysWriteExceptions_ensuresPrecededByNewline(final String
pattern) {
+ final List<PatternFormatter> 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<PatternFormatter> 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<PatternFormatter> 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 b7cee50b3a..80e4f008bf 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 RootThrowablePatternConverterTest {
@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 e178526fd9..24230dd23b 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.Nested;
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 @@ public class ThrowablePatternConverterTest {
}
@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 767bbda01a..332ee535c4 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 final class PatternParser {
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 a6211147a8..b16e9b9836 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 @@ class ThrowableStackTraceRenderer<C extends
ThrowableStackTraceRenderer.Context>
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 @@ class ThrowableStackTraceRenderer<C extends
ThrowableStackTraceRenderer.Context>
}
}
- 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<Throwable, Context.Metadata> 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 e5d2ba9ecf..7fda5791d5 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 @@ import org.apache.logging.log4j.util.PerformanceSensitive;
@PerformanceSensitive("allocation")
public final class VariablesNotEmptyReplacementConverter extends
LogEventPatternConverter {
- private final List<PatternFormatter> formatters;
+ // package private for testing
+ final List<PatternFormatter> 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 10ff9c1b2c..9134c16e34 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 class CatchingTagTest {
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 class CatchingTagTest {
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 class CatchingTagTest {
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 3cae73434c..5f864ee1fb 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 class EnterTagTest {
@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 class EnterTagTest {
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 1e2fa7d79f..3f387b15cf 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 class ExitTagTest {
@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 class ExitTagTest {
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 class ExitTagTest {
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 5fb2b419b4..ad9ab5d9ef 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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 class LoggingMessageTagSupportTest {
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 31de372033..6cd3a4eb99 100644
--- a/log4j-taglib/src/test/resources/log4j-test1.xml
+++ b/log4j-taglib/src/test/resources/log4j-test1.xml
@@ -34,7 +34,7 @@
</PatternLayout>
</File>
<List name="List">
- <PatternLayout pattern="%C{1.} %m %level M-%marker E%ex{1}"/>
+ <PatternLayout pattern="%C{1.} %m %level M-%marker E-%ex{1}"/>
</List>
</Appenders>
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 0000000000..30011263e5
--- /dev/null
+++ b/src/changelog/.2.x.x/3873_throwable_converter_new_line.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ https://logging.apache.org/xml/ns
+ https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
+ type="fixed">
+ <issue id="3873"
link="https://github.com/apache/logging-log4j2/issues/3873"/>
+ <issue id="3919"
link="https://github.com/apache/logging-log4j2/pull/3919"/>
+ <description format="asciidoc">
+ Fix Pattern Layout exception stack trace converters to no longer
prepend newlines based on context
+ </description>
+</entry>