Reputation: 935
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
Upvotes: 6
Views: 9779
Reputation: 7829
With logback:
Consider the following message:
log.debug("Current count is " + count);
We incur the cost of building the message whether the Logger logs the message or not.
Logback
offers an alternative with its parameterized messages:
log.debug("Current count is {}", count);
The braces {} will accept any Object and uses its toString() method to build a message only after verifying that the log message is required.
:
} catch (Exception e) {
logger.error("Error dividing {} by {} ", 42, zero, e);
}
Also, when an Exception is passed as the last argument to a logging method, Logback will print the stack trace for us.
Upvotes: 1
Reputation: 21404
From https://stackoverflow.com/a/45054272/122441 :
As of SLF4J 1.6.0, in the presence of multiple parameters and if the last argument in a logging statement is an exception, then SLF4J will presume that the user wants the last argument to be treated as an exception and not a simple parameter.
So, writing (in SLF4J version 1.7.x and later)
logger.error("one two three: {} {} {}", "a", "b",
"c", new Exception("something went wrong"));
Will do what you want to achieve...
Upvotes: 4
Reputation: 159114
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.
Upvotes: 8