From 294bedc6fbfc8b4b253cd4ec0399ebbb9b6897d4 Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Fri, 23 Jan 2026 11:02:26 +0900 Subject: [PATCH 1/3] test: add unit test for throwable pattern with custom exception handling Signed-off-by: Jongmin Chung --- .../core/pattern/CustomizedThrowableTest.java | 74 +++++++++++++++++++ .../resources/log4j-throwable-default.xml | 39 ++++++++++ 2 files changed, 113 insertions(+) create mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java create mode 100644 log4j-core-test/src/test/resources/log4j-throwable-default.xml diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java new file mode 100644 index 00000000000..4c36df2cee2 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java @@ -0,0 +1,74 @@ +/* + * 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.logging.log4j.core.pattern; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.test.appender.ListAppender; +import org.apache.logging.log4j.core.test.junit.LoggerContextSource; +import org.apache.logging.log4j.core.test.junit.Named; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@code throwable} pattern. + * + *

+ * ./mvnw -pl log4j-core-test -am -Dtest=org.apache.logging.log4j.core.pattern.CustomizedThrowableTest -Dsurefire.failIfNoSpecifiedTests=false test + *

+ */ +@LoggerContextSource("log4j-throwable-default.xml") +class CustomizedThrowableTest { + private ListAppender app; + private Logger logger; + + @BeforeEach + void setUp(final LoggerContext context, @Named("List") final ListAppender app) { + this.logger = context.getLogger("LoggerTest"); + this.app = app.clear(); + } + + @Test + void testException() { + final Throwable exception = new CustomException("This is an error message", "ERROR_CODE"); + logger.error("Exception", exception); + final List msgs = app.getMessages(); + assertNotNull(msgs); + assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size()); + assertTrue(msgs.get(0).contains("Code: ERROR_CODE; This is an error message"), msgs.get(0)); + } + + private static class CustomException extends Exception { + + private final String errorCode; + + public CustomException(String message, String errorCode) { + super(message); + this.errorCode = errorCode; + } + + @Override + public String toString() { + return getClass().getName() + ": Code: " + errorCode + "; " + getMessage(); + } + } +} diff --git a/log4j-core-test/src/test/resources/log4j-throwable-default.xml b/log4j-core-test/src/test/resources/log4j-throwable-default.xml new file mode 100644 index 00000000000..bf61e248eac --- /dev/null +++ b/log4j-core-test/src/test/resources/log4j-throwable-default.xml @@ -0,0 +1,39 @@ + + + + + org.junit,org.apache.maven,sun.reflect,java.lang.reflect + + + + + + + + + + + + + + + + + + + From f5ce0722f54d1271f43695bfa119e672299ff56d Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Fri, 23 Jan 2026 11:03:56 +0900 Subject: [PATCH 2/3] Revert "test: add unit test for throwable pattern with custom exception handling" This reverts commit 294bedc6fbfc8b4b253cd4ec0399ebbb9b6897d4. --- .../core/pattern/CustomizedThrowableTest.java | 74 ------------------- .../resources/log4j-throwable-default.xml | 39 ---------- 2 files changed, 113 deletions(-) delete mode 100644 log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java delete mode 100644 log4j-core-test/src/test/resources/log4j-throwable-default.xml diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java deleted file mode 100644 index 4c36df2cee2..00000000000 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/CustomizedThrowableTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.logging.log4j.core.pattern; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.List; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.test.appender.ListAppender; -import org.apache.logging.log4j.core.test.junit.LoggerContextSource; -import org.apache.logging.log4j.core.test.junit.Named; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@code throwable} pattern. - * - *

- * ./mvnw -pl log4j-core-test -am -Dtest=org.apache.logging.log4j.core.pattern.CustomizedThrowableTest -Dsurefire.failIfNoSpecifiedTests=false test - *

- */ -@LoggerContextSource("log4j-throwable-default.xml") -class CustomizedThrowableTest { - private ListAppender app; - private Logger logger; - - @BeforeEach - void setUp(final LoggerContext context, @Named("List") final ListAppender app) { - this.logger = context.getLogger("LoggerTest"); - this.app = app.clear(); - } - - @Test - void testException() { - final Throwable exception = new CustomException("This is an error message", "ERROR_CODE"); - logger.error("Exception", exception); - final List msgs = app.getMessages(); - assertNotNull(msgs); - assertEquals(1, msgs.size(), "Incorrect number of messages. Should be 1 is " + msgs.size()); - assertTrue(msgs.get(0).contains("Code: ERROR_CODE; This is an error message"), msgs.get(0)); - } - - private static class CustomException extends Exception { - - private final String errorCode; - - public CustomException(String message, String errorCode) { - super(message); - this.errorCode = errorCode; - } - - @Override - public String toString() { - return getClass().getName() + ": Code: " + errorCode + "; " + getMessage(); - } - } -} diff --git a/log4j-core-test/src/test/resources/log4j-throwable-default.xml b/log4j-core-test/src/test/resources/log4j-throwable-default.xml deleted file mode 100644 index bf61e248eac..00000000000 --- a/log4j-core-test/src/test/resources/log4j-throwable-default.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - org.junit,org.apache.maven,sun.reflect,java.lang.reflect - - - - - - - - - - - - - - - - - - - From a5a2c8219a5f4fbb88f30fb3aa57dead223163ac Mon Sep 17 00:00:00 2001 From: Jongmin Chung Date: Fri, 23 Jan 2026 11:06:25 +0900 Subject: [PATCH 3/3] fix: use Throwable.toString() for stacktrace 2.25.3 version uses Pattern Layout uses getClass().getName() + ": " + getLocalisedMessage(). but before Throwable.toString() Signed-off-by: Jongmin Chung --- .../ThrowablePatternConverterTest.java | 35 +++++++++++++++++-- .../pattern/ThrowableStackTraceRenderer.java | 7 +--- 2 files changed, 34 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..77e8457258f 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,21 @@ public String toString() { } } + private static final class CustomException extends Exception { + + private final String errorCode; + + private CustomException(final String message, final String errorCode) { + super(message); + this.errorCode = errorCode; + } + + @Override + public String toString() { + return getClass().getName() + ": Code: " + errorCode + "; " + getMessage(); + } + } + static Stream separatorTestCases() { final String level = LEVEL.toString(); return Stream.of( @@ -214,6 +229,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 = new CustomException("This is an error message", "ERROR_CODE"); + 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 +275,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 +572,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(