From 45d25bd27340fcd086bfecccc56673a5a471d3b7 Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Thu, 19 Feb 2026 13:07:51 +0900 Subject: [PATCH 1/5] fix: use Throwable.toString() for stacktrace Signed-off-by: Jongmin Chung --- .../ThrowablePatternConverterTest.java | 37 ++++++++++++++++++- .../pattern/ThrowableStackTraceRenderer.java | 7 +--- 2 files changed, 36 insertions(+), 8 deletions(-) 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 ea9294e58a4..3d42c94959e 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 @@ -79,6 +79,23 @@ public String toString() { } } + /** + * Test exception whose {@link #toString()} is intentionally overridden to return a fixed value. + */ + private static final class ToStringOverridingException extends RuntimeException { + + private static final ToStringOverridingException INSTANCE = new ToStringOverridingException(); + + private ToStringOverridingException() { + super(EXCEPTION); + } + + @Override + public String toString() { + return "foo"; + } + } + static Stream separatorTestCases() { final String level = LEVEL.toString(); return Stream.of( @@ -214,6 +231,14 @@ void full_output_should_match_Throwable_printStackTrace(final String pattern) { assertThat(actualStackTrace).as("pattern=`%s`", effectivePattern).isEqualTo(expectedStackTrace); } + @Test + void full_output_should_use_custom_toString() { + final Throwable exception = ToStringOverridingException.INSTANCE; + final String expectedStackTrace = renderStackTraceUsingJava(exception); + final String actualStackTrace = convert(patternPrefix, exception); + assertThat(actualStackTrace).isEqualTo(expectedStackTrace); + } + // This test does not provide `separator` and `suffix` options, since the reference output will be obtained from // `Throwable#printStackTrace()`, which doesn't take these into account. @ParameterizedTest @@ -252,10 +277,14 @@ private String limitLines(final String text, final int maxLineCount) { } private String renderStackTraceUsingJava() { + return renderStackTraceUsingJava(EXCEPTION); + } + + private String renderStackTraceUsingJava(final Throwable throwable) { final Charset charset = StandardCharsets.UTF_8; try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); final PrintStream printStream = new PrintStream(outputStream, false, charset.name())) { - EXCEPTION.printStackTrace(printStream); + throwable.printStackTrace(printStream); printStream.flush(); return new String(outputStream.toByteArray(), charset); } catch (final Exception error) { @@ -545,9 +574,13 @@ private static List createExceptionsOfDifferentDepths() { } static String convert(final String pattern) { + return convert(pattern, EXCEPTION); + } + + static String convert(final String pattern, final Throwable throwable) { final List patternFormatters = PATTERN_PARSER.parse(pattern, false, true, true); final LogEvent logEvent = - Log4jLogEvent.newBuilder().setThrown(EXCEPTION).setLevel(LEVEL).build(); + Log4jLogEvent.newBuilder().setThrown(throwable).setLevel(LEVEL).build(); final StringBuilder buffer = new StringBuilder(); for (final PatternFormatter patternFormatter : patternFormatters) { patternFormatter.format(logEvent, buffer); 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 885d7de36e8..4d210213213 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 @@ -138,12 +138,7 @@ private void renderCause( } static void renderThrowableMessage(final StringBuilder buffer, final Throwable throwable) { - final String message = throwable.getLocalizedMessage(); - buffer.append(throwable.getClass().getName()); - if (message != null) { - buffer.append(": "); - buffer.append(message); - } + buffer.append(throwable); } final void renderStackTraceElements( From 16eb60b94a505526249eb7d7992b9ddea7537632 Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Thu, 19 Feb 2026 13:08:03 +0900 Subject: [PATCH 2/5] fix: EventParameterMemoryLeakTest uses toString() Signed-off-by: Jongmin Chung --- .../logging/log4j/core/EventParameterMemoryLeakTest.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java index ff08d2b2cf8..d8b1d90cf2b 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java @@ -59,11 +59,8 @@ void parameters_should_be_garbage_collected(final TestInfo testInfo) throws Thro assertThat(messages).hasSize(4); assertThat(messages.get(0)).isEqualTo("Message with parameter %s", parameter.value); assertThat(messages.get(1)).isEqualTo(parameter.value); - assertThat(messages.get(2)) - .startsWith(String.format("test%n%s: %s", ObjectThrowable.class.getName(), parameter.value)); - assertThat(messages.get(3)) - .startsWith( - String.format("test hello%n%s: %s", ObjectThrowable.class.getName(), parameter.value)); + assertThat(messages.get(2)).startsWith(String.format("test%n%s", new ObjectThrowable(parameter))); + assertThat(messages.get(3)).startsWith(String.format("test hello%n%s", new ObjectThrowable(parameter))); // Return the GC subject return parameter; From 7e50daf972c3077a84686ca8d284cdfa551e2fd8 Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Sun, 1 Mar 2026 09:09:16 +0900 Subject: [PATCH 3/5] fix: change convert private level Signed-off-by: Jongmin Chung --- .../log4j/core/pattern/ThrowablePatternConverterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3d42c94959e..0ce38d03237 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 @@ -577,7 +577,7 @@ static String convert(final String pattern) { return convert(pattern, EXCEPTION); } - static String convert(final String pattern, final Throwable throwable) { + private static String convert(final String pattern, final Throwable throwable) { final List patternFormatters = PATTERN_PARSER.parse(pattern, false, true, true); final LogEvent logEvent = Log4jLogEvent.newBuilder().setThrown(throwable).setLevel(LEVEL).build(); From e23e516356a0fad32b20c08ab594828c1b34094e Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Sun, 1 Mar 2026 09:32:12 +0900 Subject: [PATCH 4/5] chore: add changelog Signed-off-by: Jongmin Chung --- .../.2.x.x/4033_fix_custom_throwable_to_sting.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml diff --git a/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml b/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml new file mode 100644 index 00000000000..d52684ac652 --- /dev/null +++ b/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml @@ -0,0 +1,13 @@ + + + + + + Take Throwable#toString() into account while rendering stack traces in Pattern Layout. + + From 217cedb23c09e1e35e6a40934ca0afabc240ff52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 2 Mar 2026 10:43:19 +0100 Subject: [PATCH 5/5] Update changelog --- src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml b/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml index d52684ac652..761380f81c1 100644 --- a/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml +++ b/src/changelog/.2.x.x/4033_fix_custom_throwable_to_sting.xml @@ -5,9 +5,9 @@ https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd" type="changed"> - + - Take Throwable#toString() into account while rendering stack traces in Pattern Layout. + Take `Throwable#toString()` into account while rendering stack traces in Pattern Layout.