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

时间:2018-07-18 03:03:39

标签: java logging slf4j slf4j-api

在网上the documentationStack 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,最后一个选项不正确,因为:

  

因为printf样式的格式字符串是在运行时解释的,所以   而不是由编译器验证的结果,它们可能包含导致   创建了错误的字符串。此规则静态验证   在以下情况下,printf样式格式字符串与其参数的关联   调用java.util.Formatter的format(...)方法,   java.lang.String,java.io.PrintStream,MessageFormat和   java.io.PrintWriter类和的printf(...)方法   java.io.PrintStream或java.io.PrintWriter类。

AppException类如下:

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

3 个答案:

答案 0 :(得分:4)

您认为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中,如果将鼠标悬停在方法调用上,它将确切显示正在调用的方法。当有过载时非常有用。


  

我只是重新构建了代码,并添加了两个日志条目:一个调用toString,另一个调用您提议的强制转换。进行投射的人未按预期工作,并且产生了我最初发布的结果(无消息)

无法复制。这是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

首次调用按原样(使用{})和堆栈跟踪打印消息。
第二次呼叫打印消息,其中{}替换为异常文本。

答案 1 :(得分:0)

来自https://stackoverflow.com/a/45054272/122441

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

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

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

将做您想实现的目标...

答案 2 :(得分:0)

使用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);
}
<块引用>

此外,当异常作为最后一个参数传递给日志记录时 方法,Logback 会为我们打印堆栈跟踪。