Java 如何在slf4j中格式化异常?
从我在网上找到的许多示例中,在slf4j中连接字符串的正确方法是使用内置的字符串格式 例如:Java 如何在slf4j中格式化异常?,java,logging,slf4j,slf4j-api,Java,Logging,Slf4j,Slf4j Api,从我在网上找到的许多示例中,在slf4j中连接字符串的正确方法是使用内置的字符串格式 例如: LOGGER.error("ExceptionHandler throws {}" , appException); 我还尝试了无格式设置,结果相同: LOGGER.error("ExceptionHandler throws " , appException); 出于某种原因,这对我来说不起作用,我不知道我错过了什么。如果传递对象,是否使用不同的格式 上面的示例打印以下日志消息:
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()时
根据Sonar的说法,最后一个选项是不正确的,因为:
因为printf样式的格式字符串是在运行时解释的,而不是
如果未经编译器验证,则它们可能包含导致错误的错误
正在创建错误的字符串。此规则静态验证
当
调用java.util.Formatter的format(…)方法,
java.lang.String、java.io.PrintStream、MessageFormat和
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);
我使用的是
slf4japi 1.7.22
您相信的LOGGER.error(“ExceptionHandler抛出{}”,appException)代码>正在调用:
但它实际上是在呼吁:
因为这更适合参数类型
如果希望它调用第一个,请将参数强制转换为对象
:
LOGGER.error("ExceptionHandler throws {}" , (Object) appException);
或者像您已经尝试过的那样调用toString()
如果您使用了一个好的IDE来编写代码,IDE会帮助您解决这个问题。例如,在Eclipse中,如果将鼠标悬停在方法调用上,它将准确地显示正在调用的方法。当存在过载时非常有用
我刚刚重建了代码,并添加了两个日志条目:一个是调用toString,另一个是您建议的强制转换。有铸造的那个没有按预期工作,正在产生我最初发布的结果(没有消息)
无法复制。这里是MCVE(使用org.slf4j:slf4j simple:1.7.25
):
输出
[main]错误测试-测试1:{}
java.lang.Exception:测试
at Test.main(Test.java:8)
[main]错误测试-测试2:java.lang.Exception:测试
第一次调用按原样打印消息(带有{}
)和堆栈跟踪。
第二次呼叫打印消息,将{}
替换为异常文本。来自:
从SLF4J 1.6.0开始,在存在多个参数的情况下,如果日志语句中的最后一个参数是异常,则SLF4J将假定用户希望最后一个参数被视为异常,而不是简单参数
因此,编写(在SLF4J版本1.7.x和更高版本中)
将通过:
请考虑以下信息:
log.debug("Current count is " + count);
无论记录器是否记录消息,我们都要承担构建消息的成本。
Logback
提供了一种可选的参数化消息:
大括号{}将接受任何对象,并仅在验证日志消息是必需的之后,才使用其toString()方法生成消息。
:
另外,当异常作为最后一个参数传递给日志记录
方法,Logback将为我们打印堆栈跟踪
为什么要投反对票?你对方法调用的看法是正确的。在检查eclipse之后,它实际上调用了错误的方法。我应用了这个建议,但不幸的是它仍然给出了相同的结果result@Yeikel然后您没有编译代码,没有部署代码,或者您正在查看一个旧的日志条目。(我自己做过几次最后一个,所以现在我通常在重试之前删除日志文件,这样我就不会再犯那种错误)我只是重新生成代码,并添加了两个日志条目:一个是调用toString,另一个是您建议的强制转换。有铸造的那个并没有像预期的那样工作,并且产生了我最初发布的结果(没有消息)。一个完全错误的答案被提升。所描述的行为与SLF4J无关,可能是您对记录器实现感到困惑。实际上,在SLF4J 1.7.x中,记录器接口有方法签名:public void error(字符串格式、对象…参数)代码>这显然与最后一个异常是否可丢弃无关。这要由实际的记录器实现来处理。
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)
logger.error("one two three: {} {} {}", "a", "b",
"c", new Exception("something went wrong"));
log.debug("Current count is " + count);
log.debug("Current count is {}", count);
} catch (Exception e) {
logger.error("Error dividing {} by {} ", 42, zero, e);
}