你如何在 slf4j 中格式化异常?

How do you format an exception in slf4j?

从我在网上找到的许多示例中,the documentation and here in Stack overflow,在 slf4j 中连接字符串的正确方法是使用内置的字符串格式。

例如:

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

我也试过不格式化,结果是一样的:

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

出于某种原因,这对我不起作用,我不知道我错过了什么。如果我们传递一个对象,我们会使用不同的格式吗?

上面的示例正在打印以下日志消息:

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

而不是我在使用常规连接时收到的预期消息:

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

或者当我手动调用 .toString()

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

根据 Sonar 的说法,最后一个选项不正确,因为:

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.

AppException class 如下:

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 + "]";
    }

}

我正在构建我的记录器如下:

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

我正在使用 slf4j-api 1.7.22

您认为 LOGGER.error("ExceptionHandler throws {}" , appException); 正在呼叫:

error(String format, Object arg)

但实际上是调用:

error(String msg, Throwable t)

因为这更适合参数类型。

如果你想让它调用第一个,将参数转换为 Object:

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

或像您已经尝试过的那样致电 toString()

如果您使用好的 IDE 编写代码,IDE 会帮助您解决这个问题。例如。在 Eclipse 中,如果将鼠标悬停在方法调用上,它会准确显示正在调用的方法。当存在过载时非常有用。


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)

无法重现。这是 MCVE(使用 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)

输出

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

第一次调用按原样打印消息({})和堆栈跟踪。
第二次调用打印消息 {} 替换为异常文本。

来自 :

从 SLF4J 1.6.0 开始,在存在多个参数的情况下,如果日志语句中的最后一个参数是异常,则 SLF4J 将假定用户希望将最后一个参数视为异常,而不是一个简单的参数。

因此,编写(在 SLF4J 版本 1.7.x 及更高版本中)

logger.error("one two three: {} {} {}", "a", "b", 
          "c", new Exception("something went wrong"));

会做你想做的事...

logback:

考虑以下消息:

log.debug("Current count is " + count);

无论 Logger 是否记录消息,我们都会产生构建消息的 成本Logback 提供了另一种选择,其 参数化消息:

log.debug("Current count is {}", count);

大括号 {} 将接受 任何对象 并使用其 toString() 方法构建消息 仅在验证日志消息是必需的之后。

:

} 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.