Skip to content

ThrowableAttributeConverter does not round-trip exception messages containing line breaks #4343

Description

@ppkarwasz

Description

ThrowableAttributeConverter (log4j-jpa) cannot round-trip a Throwable whose message contains line breaks.

convertToDatabaseColumn writes Throwable.toString() verbatim, one stack trace frame per line.
convertToEntityAttribute splits the column on line breaks and interprets it by line prefix:

  • any line starting with Caused by starts a new cause, whose class is loaded by name;
  • every other line is parsed as a stack trace frame by StackTraceElementAttributeConverter.

Since exception messages are arbitrary text, a message such as

first line
Caused by com.example.SomeClass: second line

is read back as a cause of type com.example.SomeClass, and other lines make the frame parser throw.
Moreover, getThrowable loads the class with LoaderUtil.loadClass, which initializes it
before checking that it is a Throwable subclass.

Expected behavior:

  1. convertToDatabaseColumn escapes line breaks in the message (and convertToEntityAttribute unescapes them),
    so the column layout does not depend on the message content.
  2. getThrowable loads the class without initializing it (Class.forName(name, false, loader))
    and checks Throwable.class.isAssignableFrom before instantiating it.
  3. A line that cannot be parsed as a stack trace frame does not fail the whole conversion.

Configuration

Version: 2.26.1 (and 2.x at d631e82)

Operating system: any

JDK: any

Logs

A message line that is neither a frame nor a Caused by line makes
StackTraceElementAttributeConverter.convertString fail with a StringIndexOutOfBoundsException.

Reproduction

@Test
void messageWithLineBreaksRoundTrips() {
    final ThrowableAttributeConverter converter = new ThrowableAttributeConverter();
    final Throwable original = new IllegalStateException("first line\nCaused by java.lang.Error: second line");

    final Throwable restored = converter.convertToEntityAttribute(converter.convertToDatabaseColumn(original));

    assertThat(restored).isInstanceOf(IllegalStateException.class);
    assertThat(restored.getMessage()).isEqualTo(original.getMessage());
    assertThat(restored.getCause()).isNull();
}

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions