4

从我在网上找到的许多示例、文档和此处的堆栈溢出中,在 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 样式的格式字符串是在运行时解释的,而不是由编译器验证的,所以它们可能包含导致创建错误字符串的错误。此规则在调用 java.util.Formatter、java.lang.String、java.io.PrintStream、MessageFormat 和 java.io 的 format(...) 方法时静态验证 printf 样式格式字符串与其参数的相关性.PrintWriter 类和 java.io.PrintStream 或 java.io.PrintWriter 类的 printf(...) 方法。

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

4

3 回答 3

6

你相信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

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

于 2018-07-18T03:14:49.180 回答
2

来自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"));

会做你想做的事...

于 2018-12-26T15:48:04.230 回答
0

使用logback

考虑以下消息:

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

无论 Logger 是否记录消息, 我们都会产生构建消息的成本。通过其参数化消息Logback提供替代方案:

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

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

} catch (Exception e) {
    logger.error("Error dividing {} by {} ", 42, zero, e);
}

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

于 2021-01-04T02:30:16.627 回答