emrekgn
emrekgn

Reputation: 682

Logback - How to write custom exception converter to collapse stack trace into one line

I'm using SpringBoot 2.4.8 (and it uses logback-classic 1.2.3) and I want to configure a custom converter that collapses multiline stack trace into one line (same as this question).

Let's say I have this code snippet that intentionally throws an Exception for test purposes:

package co.foo.bar.test;

// ...

@Slf4j
public class Foo {

  public void bar() {
  // ...
    try {
      Integer.parseInt(null);
    } catch (Exception e) {
      log.error(e.getLocalizedMessage(), e);
    }
  }

}

When I have defined the conversionRule and added the %ex symbolic to the pattern, logback just ignores the ERROR log:

logback.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <conversionRule conversionWord="ex" converterClass="co.foo.bar.logging.CompressedStackTraceConverter" />

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>
        %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg %ex%n
      </pattern>
    </encoder>
  </appender>

  // ...

  <logger name="co.foo.bar.test" additivity="false" level="INFO">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="MESSAGING_LOG"/>
  </logger>

</configuration>

CompressedStackTraceConverter.java:

package co.foo.bar.logging;

// ...

public class CompressedStackTraceConverter extends ThrowableProxyConverter {
  public CompressedStackTraceConverter() {
  }

  protected String throwableProxyToString(IThrowableProxy tp) {
    String original = super.throwableProxyToString(tp);
    return original.replaceAll("\\R\\t?", " ~~ ");
  }
}

When I remove the %ex symbolic, logback continues to print ERROR logs so the logback.xml should be correct. And when I debug the custom converter class, I can see it can successfully return a one-line exception string whenever an exception occurs.

I'm pretty sure there is a simple solution to this but cannot figure it out yet. What am I missing here?

Thanks in advance.

Upvotes: 2

Views: 4872

Answers (2)

hughjdavey
hughjdavey

Reputation: 1140

Since your custom converter is just a String#replace call, you may be able to use the solution mentioned in this blogpost.

This can go in the application.properties file.

# Single line. Full stack trace will be captured in a single line
logging.exception-conversion-word=%replace(%wEx){'\n','\u2028'}%nopex

# Truncated Single line. Only the exception message will be captured in a single line
logging.exception-conversion-word=%replace(%wEx{short}){'\n','\u2028'}%nopex

# Truncated. Default formatting but only capturing a single line
logging.exception-conversion-word=%wEx{short}

This works if you don’t override logging.pattern.console as the default logging.pattern.console allows you to use logging.exception-converstion-word

In my case I had to use \r instead of \n like in the above CompressedStackTraceConverter class, presumably because of being on windows.

My final expression in my application.yml was

logging:
  exception-conversion-word: "%replace(%wEx){'(\n|\r)\t?','\u2028'}%nopex"

which includes optional matching of the tab as in the above CompressedStackTraceConverter class. The quotes were needed for the YAML parser.

Upvotes: 1

emrekgn
emrekgn

Reputation: 682

I think there may be another (newer?) way to achieve this than the one described in the related question or there is a bug in this logback version.

Instead, I ended up using logstash-logback-encoder to print everything as a one-line JSON string.

Here is the example if anyone is interested:

  • pom.xml:
<dependency>
  <groupId>net.logstash.logback</groupId>
  <artifactId>logstash-logback-encoder</artifactId>
  <version>6.6</version>
</dependency>
  • logback.xml:
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
      <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
        <maxDepthPerThrowable>30</maxDepthPerThrowable>
        <maxLength>2048</maxLength>
        <rootCauseFirst>true</rootCauseFirst>
        <inlineHash>true</inlineHash>
      </throwableConverter>
      <shortenedLoggerNameLength>36</shortenedLoggerNameLength>
      <timeZone>UTC</timeZone>
    </encoder>
  </appender>

Upvotes: 1

Related Questions