Skip to content

Conversation

@JongminChung
Copy link

@JongminChung JongminChung commented Jan 23, 2026

Context

Issue #4019 reports that Pattern Layout no longer preserves custom Throwable.toString() output after upgrading from 2.24.3 to 2.25.x.
The official docs for the exception converter state that the default behavior (no options) should match Throwable#printStackTrace(), which relies on Throwable.toString() for the header line.

Suspected change: PR #2691

PR #2691 (commit b55c4d3fa2 - "Consolidate stack trace rendering in Pattern Layout") introduced a new renderer pipeline:

  • Added ThrowableStackTraceRenderer (and extended/inverted variants) to manually render stack traces.
  • Updated ThrowablePatternConverter to delegate to the new renderer instead of calling Throwable#printStackTrace() directly.

What changed in rendering behavior

Before PR #2691 (e.g., tag rel/2.24.3):

  • ThrowablePatternConverter used Throwable#printStackTrace() directly (either into a StringWriter or a StringBuilderWriter).
  • The output header line was always produced by Throwable.toString().
  • Any custom override of toString() or printStackTrace() would be reflected in Pattern Layout output.

After PR #2691:

  • Rendering moved into ThrowableStackTraceRenderer which builds the header line manually.
  • In ThrowableStackTraceRenderer.renderThrowableMessage(...), the header was constructed as:
    • throwable.getClass().getName() + ": " + throwable.getLocalizedMessage()
  • This bypasses Throwable.toString() entirely.
  • The new renderer also manually iterates stack trace elements, suppressed exceptions, and causes using cached metadata.

Why that causes #4019

Throwable#printStackTrace() uses Throwable.toString() for its first line.
By replacing printStackTrace() with a manual renderer that uses getClass().getName() and getLocalizedMessage() , any customized toString() logic is lost.

This matches the reporter's symptoms: stack traces remain, but the custom header details disappear.

In short:

  • Regression introduced by PR Consolidate stack trace rendering in Pattern Layout #2691: default %ex output no longer matches printStackTrace() for custom toString() implementations.
  • Root cause: renderThrowableMessage changed from toString() semantics to explicit class/message concatenation.

Test case

I added test case that issue reporter gives.


Related issue: #3623 (Pattern Layout vs. JSON Template Layout)

Issue #3623 reports a mismatch between Pattern Layout (%ex) and JSON Template Layout (exception resolver):

  • JSON Template Layout uses Throwable.printStackTrace() for stackTrace.stringified.
  • Pattern Layout uses ThrowableStackTraceRenderer.renderThrowableMessage(...).

The JSON Template Layout implementation lives in:

  • log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java

Specifically, StackTraceStringResolver.resolve(...) calls throwable.printStackTrace(...) (line ~84), which means its header line always comes from Throwable.toString().

This makes the mismatch visible when exceptions override toString(). Aligning ThrowableStackTraceRenderer to throwable.toString() closes both #4019 (regression) and #3623 (layout inconsistency).

Checklist

  • Base your changes on 2.x branch if you are targeting Log4j 2; use main otherwise
  • ./mvnw verify succeeds (the build instructions)
  • Non-trivial changes contain an entry file in the src/changelog/.2.x.x directory
  • Tests are provided

Signed-off-by: Jongmin Chung <chungjm0711@gmail.com>
2.25.3 version uses Pattern Layout uses getClass().getName() + ": " +
getLocalisedMessage().

but before Throwable.toString()

Signed-off-by: Jongmin Chung <chungjm0711@gmail.com>
buffer.append(": ");
buffer.append(message);
}
buffer.append(throwable);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant