ProtossShuttle
ProtossShuttle

Reputation: 1673

Logback - How to log the simple name of an exception separately

Is there any way you can log only the simple name of an exception without explicitly retrieving it from the code?

For example, by calling

log.error(exception);

with a logback pattern

%d{yyyy-MM-dd}|%-5level|%m%n

instead of just logging the exception stack traces

2018-01-01|ERROR|
mainPackage.foo.bar.RocketExplosionException: Houston we have a problem
  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)

A separate column with the simple name of the exception is expected to be logged

2018-01-01|ERROR|RocketExplosionException|
mainPackage.foo.bar.RocketExplosionException: Houston we have a problem
  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)

Upvotes: 4

Views: 5649

Answers (2)

glytching
glytching

Reputation: 47905

You could write your own custom conversion specifier.

To do this, you would declare a conversion rule in your logback.xml for the %exname symbolic like so:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <conversionRule conversionWord="exname" converterClass="com.foo.ExceptionNameConverter" />

    ...

</configuration>

Then declare ExceptionNameConverter like so:

import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.IThrowableProxy;

public class ExceptionNameConverter extends ThrowableProxyConverter {
    @Override
    protected String throwableProxyToString(IThrowableProxy tp) {
        return tp.getClassName();
    }
}

Now, using this pattern:

%d{yyyy-MM-dd}|%-5level|%exname|%m%n

The following log statement:

logger.error("Boom!", new RuntimeException("ouch"));

Will emit:

2018-09-26|ERROR|java.lang.RuntimeException|Boom!

Upvotes: 5

ProtossShuttle
ProtossShuttle

Reputation: 1673

As is Andreas suggests, one way to support exception name logging is use a custom Layout instead of PatternLayout

A sample layout:

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;

public class ErrorLoggingLayout extends LayoutBase<ILoggingEvent> {

    private static final char SEP = '|';

    @Override
    public String doLayout(final ILoggingEvent event) {
        StringBuilder sb = new StringBuilder(128);
        sb.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
        sb.append(SEP);
        sb.append(event.getLevel());
        sb.append(SEP);
        sb.append(event.getThreadName());
        sb.append(SEP);
        StackTraceElement frame = event.getCallerData()[0];
        sb.append(extractSimpleName(frame.getClassName())).append('.').append(frame.getMethodName()).append(':').append(frame.getLineNumber());
        sb.append(SEP);
        IThrowableProxy throwableProxy = event.getThrowableProxy();
        if (throwableProxy != null) {
            String simpleName = extractSimpleName(throwableProxy.getClassName());
            sb.append(simpleName);
        }
        sb.append(SEP);
        sb.append(event.getFormattedMessage());
        sb.append(CoreConstants.LINE_SEPARATOR);
        if (throwableProxy != null) {
            sb.append(ThrowableProxyUtil.asString(throwableProxy));
        }
        return sb.toString();

    }

    @NotNull
    private String extractSimpleName(final String cName) {
        int index = cName.lastIndexOf('.');
        return cName.substring(index + 1);
    }
}

This result turns out like

7645|ERROR|Thread-Name-0|RocketConsole.report:22|RocketExplosionException|custom msg
mainPackage.foo.bar.RocketExplosionException: Houston we have a problem
  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)

However, one shortage of this approach is that the output format might be slightly different than the original pattern, and it takes extra effort to format the fields.

Upvotes: 0

Related Questions