Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you format an exception in slf4j?

From many examples I found online , the documentation and here in Stack overflow , the right way to concatenate strings in slf4j is to use the built-in string formatting.

For example :

    LOGGER.error("ExceptionHandler throws {}" , appException);

I also tried with no formatting and it produced the same result :

    LOGGER.error("ExceptionHandler throws " , appException);

For some reason , that is not working for me and I don't know what I am missing. Do we use a different format if we pass an object?

The examples above are printing the following log message :

2018-07-18 02:38:19 ERROR c.a.c.c.p.ExceptionProcessor:67 - ExceptionHandler throws  {}

Instead of the expected message that I get when I use regular concatenation:

LOGGER.error("ExceptionHandler throws " + appException);

OR when I manually call .toString()

   LOGGER.error("ExceptionHandler throws {}" , appException.toString());

According to Sonar , this last option is not correct because :

Because printf-style format strings are interpreted at runtime, rather than validated by the compiler, they can contain errors that result in the wrong strings being created. This rule statically validates the correlation of printf-style format strings to their arguments when calling the format(...) methods of java.util.Formatter, java.lang.String, java.io.PrintStream, MessageFormat, and java.io.PrintWriter classes and the printf(...) methods of java.io.PrintStream or java.io.PrintWriter classes.

The AppException class is the following :

import java.io.Serializable;

import javax.ws.rs.core.Response.Status;


public class AppException extends Exception implements Serializable {

    private static final long serialVersionUID = 1L;

    private Error error;
    private Status status;

    public AppException(Error error, Status status) {
        this.error = error;
        this.status = status;
    }

    public AppException() {
    }

    public Error getError() {
        return error;
    }

    public void setError(Error error) {
        this.error = error;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "AppException [error=" + error + ", status=" + status + "]";
    }

}

I am building my logger as follow :

private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionProcessor.class);

And I am using slf4j-api 1.7.22

like image 589
Yeikel Avatar asked Mar 06 '23 19:03

Yeikel


1 Answers

You believe LOGGER.error("ExceptionHandler throws {}" , appException); is calling:

error(String format, Object arg)

but it is actually calling:

error(String msg, Throwable t)

because that is a better fit for the argument types.

If you want it to call the first one, cast the argument to Object:

LOGGER.error("ExceptionHandler throws {}" , (Object) appException);

or call toString() like you already tried.

If you had used a good IDE to write your code, the IDE would have helped you figure that out. E.g. in Eclipse, if you hover the mouse over the method call, it'll show you exactly which method is being called. Very useful when there are overloads in place.


I just rebuilt the code , and added two log entries : one with a call to toString and another with the casting you are proposing. The one with the casting is not working as expected and is producing the result I posted initially (no message)

Unable to reproduce. Here is MCVE (using org.slf4j:slf4j-simple:1.7.25):

Logger logger = LoggerFactory.getLogger("Test");
Exception e = new Exception("Test");
    
logger.error("Test 1: {}", e);           // calls error(String, Throwable)
logger.error("Test 2: {}", (Object) e);  // calls error(String, Object)

Output

[main] ERROR Test - Test 1: {}
java.lang.Exception: Test
    at Test.main(Test.java:8)
[main] ERROR Test - Test 2: java.lang.Exception: Test

First call prints message as-is (with {}) and the stacktrace.
Second call print message with {} replaced by exception text.

like image 102
Andreas Avatar answered Mar 16 '23 00:03

Andreas