在网上the documentation和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,最后一个选项不正确,因为:
因为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
答案 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 会为我们打印堆栈跟踪。