如何记录格式化消息、对象数组和异常?

317

如何正确记录既包含已填充消息又包含异常堆栈跟踪的日志?

logger.error(
    "\ncontext info one two three: {} {} {}\n",
    new Object[] {"1", "2", "3"},
    new Exception("something went wrong"));

我想要制作一个类似于这样的输出:

context info one two three: 1 2 3
java.lang.Exception: something went wrong
stacktrace 0
stacktrace 1
stacktrace ...

我的SLF4J版本是1.6.1。


3
我不明白为什么 slf4j 要使用自己的格式字符串语法,而不是标准的 %s 风格。很烦人。 - Keith Tyler
3
@KeithTyler 我更喜欢 {},这是品味的问题... - Betlista
3
@KeithTyler 的 toString() 方法可能是昂贵的。使用这种语法,只传递每个对象的引用,并且仅在实际记录特定消息时才调用 toString() 方法。如果日志级别为 WARN 或更高,则不会调用 info() 日志调用中引用的对象的 toString() 方法。{} 语法提醒用户,这不是类似于 String.format() 的操作,即它们应该传递对象而不是其字符串表示形式。 - user149408
3个回答

512

从 SLF4J 1.6.0 开始,如果日志语句中存在多个参数,并且最后一个参数是异常类型,那么 SLF4J 将假定用户希望将最后一个参数视为异常而不是普通参数。 另请参见相关的 FAQ 条目

因此,在 SLF4J 版本 1.7.x 及更高版本中编写日志时:

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

或者在 SLF4J 版本 1.6.x 中编写

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

将产生

one two three: a b c
java.lang.Exception: something went wrong
    at Example.main(Example.java:13)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at ...

输出的确切内容取决于底层框架(例如logback、log4j等)以及底层框架的配置方式。但是,如果最后一个参数是异常,无论底层框架如何配置,它都将被解释为异常。


4
你使用的是哪个底层日志框架?如我之前所述,如果最后一个参数是异常信息,无论底层框架如何,它都会被解释为异常信息。(已经在logback、slf4j-log4j12、slf4j-jdk14和slf4j-simple中进行了测试。) - Ceki
3
很抱歉,我注意到您在示例中使用了n=3个占位符和n+1=4个对象数组元素,而我的期望是在格式化字符串中有n个占位符和n个对象数组元素,再加上一个异常作为第三个参数。我原本期望这个异常会带有堆栈跟踪信息被打印出来,但事实并非如此。请问这是按设计要求运行的吗?另外,如果我在格式化字符串中使用n个占位符和n个对象数组元素,并将异常作为最后一个元素放入数组中,我无法看到任何堆栈跟踪信息。也许需要更强调一下n个占位符和n+1个对象在数组中的情况。 - rowe
8
我本打算向@Ceki提出质疑,因为它不在Javadocs中,但它在Logger Javadoc类的顶部:http://www.slf4j.org/apidocs/org/slf4j/Logger.html - Adam Gent
2
我创建了一个改进请求,如果你喜欢的话,可以为它投票。 - Betlista
1
你知道如何让Intellij IDEA 2020.2.3不再抱怨这个问题吗?它会给出警告:“格式化的日志消息需要N个参数,但只传递了N-1个”,因此我被迫使用String.format() - dfche

10
除了@Ceki的回答外,如果您正在使用logback并在项目中设置配置文件(通常是logback.xml),您还可以定义日志以绘制堆栈跟踪。
<encoder>
    <pattern>%date |%-5level| [%thread] [%file:%line] - %msg%n%ex{full}</pattern> 
</encoder>

模式中的%ex是使它与众不同的地方


0

接受的答案很好。我在这里补充一下我的情况,现在由于答案而工作。这可能会帮助其他人。

我正在使用SLF4Jlogback与JSON编码器。此外,我使用markerarguments来丰富我的输出。

    logger.error(getMarker("errorEvent"),
                 "An error occurred",
                 entries(mapOf("someKey" to "someValue")),
                 new Exception())

输出:
  {
   "level": "ERROR",
   "event": "errorEvent",
   "eventData": {
      "someKey": "someValue"
   },
   "stacktrace": "...省略...",
   "message": "发生了一个错误"
}
当然,logstash有很多配置在幕后,但我只想展示传递为entriesarguments在配置的eventData标签中显示。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接