In this post we’re going to implement the exception logging with AspectJ’s AfterThrowing advice. That approach is used in EasyLog

Let’s say we need to log the exceptions that are thrown by any method annotated with the annotation @LogIt.

Then the annotation for the @AfterThrowing advice will look like this

@AfterThrowing(pointcut = "execution(* *(..)) && @annotation(annotation)", throwing = "e")

We should specify what annotation we mean in advice’s method signature

Let’s implement that advice

@AfterThrowing(pointcut = "execution(* *(..)) && @annotation(annotation)", throwing = "e")
public void logException(JoinPoint jp, LogIt annotation, Throwable e) {
	logger.error("{}\r\n in {}: ",
            e.toString(),
            jp.getSignature().toShortString(),
            e);
}

Now if we try for example to divide by zero in the method annotated with @LogIt we will get this

08:09:19.023 [main] ERROR  io.lenar.easy.log.ExceptionLogger - java.lang.ArithmeticException: / by zero 
 in Universe.getStarsBeforeBigBang(): 
java.lang.ArithmeticException: / by zero
	at io.lenar.examples.model.Universe.getStarsBeforeBigBang_aroundBody4(Universe.java:50) [classes/:na]
	at io.lenar.examples.model.Universe$AjcClosure5.run(Universe.java:1) ~[classes/:na]
	at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:221) ~[aspectjrt-1.8.7.jar:na]
	at io.lenar.easy.log.UneasyLogger.logMethod(UneasyLogger.java:35) ~[easy-log-1.1.5-SNAPSHOT.jar:na]
	at io.lenar.easy.log.EasyLoggerNoSpring.logItMethodLevel(EasyLoggerNoSpring.java:22) ~[easy-log-1.1.5-SNAPSHOT.jar:na]
	at io.lenar.examples.log.MyLogger.methodLog(MyLogger.java:22) ~[classes/:na]
	at io.lenar.examples.model.Universe.getStarsBeforeBigBang(Universe.java:49) [classes/:na]
	at io.lenar.examples.LoggerTest.exceptionTest(LoggerTest.java:32) [test-classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_152]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_152]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_152]
...

Logging WebApplicationException

What if we want to log some exceptions different way. For example when we log a web service client calls we don’t need all that stacktrace, we need to log the error message instead. Let’s do that.

First of all we need to check if this is a WebApplicationException

if (e instanceof WebApplicationException) {
    logWebApplicationException((WebApplicationException) e, jp, annotation.label());
}

In logWebApplicationException we read the body of the Http response if that exists and log it.

private void logWebApplicationException(WebApplicationException webApplicationException, JoinPoint jp, String label) {
    Response response = webApplicationException.getResponse();
    boolean hasResponseBody;
    try {
        hasResponseBody = response.hasEntity();
    } catch (IllegalStateException ise) {
        hasResponseBody = false;
    }
    if (hasResponseBody) {
        response.bufferEntity();
        logger.error("{}\r\n {}: \r\n{}",
                webApplicationException.toString(),
                jp.getSignature().toShortString(),
                response.readEntity(String.class));
    } else {
        logger.error("{}\r\n {}: \r\n",
                webApplicationException.toString(),
                jp.getSignature().toShortString());
    }
}

Now if we failed to login for example we will get this in the logs

08:34:40.682 [main] ERROR io.lenar.easy.log.ExceptionLogger - javax.ws.rs.ForbiddenException: HTTP 403 Forbidden
UserWebServiceClient.login(..): 
{"errorMessage":"Invalid LoginID","errorDetail":"invalid loginID or password","errorCode":403}

Complete example

@Aspect
public class ExceptionLogger {

	private static org.slf4j.Logger logger = LoggerFactory.getLogger(ExceptionLogger.class);

	@AfterThrowing(pointcut = "execution(* *(..)) && @annotation(annotation)", throwing = "e")
	public void logException(JoinPoint jp, LogIt annotation, Throwable e) {
        if (e instanceof WebApplicationException) {
            logWebApplicationException((WebApplicationException) e, jp, annotation.label());
            return;
        }

        logOtherException(e, jp, annotation.label());
	}

    private void logWebApplicationException(WebApplicationException webApplicationException, JoinPoint jp, String label) {
        Response response = webApplicationException.getResponse();
        boolean hasResponseBody;
        try {
            hasResponseBody = response.hasEntity();
        } catch (IllegalStateException ise) {
            hasResponseBody = false;
        }
        if (hasResponseBody) {
            response.bufferEntity();
            logger.error("{}\r\n {}: \r\n{}",
                    webApplicationException.toString(),
                    jp.getSignature().toShortString(),
                    response.readEntity(String.class));
        } else {
            logger.error("{}\r\n {}: \r\n",
                    webApplicationException.toString(),
                    jp.getSignature().toShortString());
        }
    }

    private void logOtherException(Throwable e, JoinPoint jp, String label) {
		logger.error("{}\r\n in {}: ",
	            e.toString(),
	            jp.getSignature().toShortString(),
	            e);
    }
}

You may also find these posts interesting: