log.error()不打印堆栈是因为未传入Throwable参数;正确写法是log.error("消息", exception),异常必须作为最后一个参数传递,否则仅输出toString()结果且丢失堆栈。

Java里log.error()为什么没打印出异常堆栈?
直接调用 log.error("发生错误") 不会自动输出异常的堆栈信息,只记录字符串。必须显式传入 Throwable 实例,日志框架(如 Logback、Log4j2)才会解析并格式化堆栈。
- 正确写法:
log.error("数据库查询失败", e)—— 第二个参数是Exception对象 - 错误写法:
log.error("数据库查询失败: " + e.getMessage())—— 堆栈完全丢失 - 如果用了 SLF4J 接口,确保底层绑定的是支持异常对象传递的实现(Logback 默认支持;Log4j2 需确认版本 ≥ 2.16.0)
如何在catch块中正确记录异常和上下文?
只记异常本身往往不够,业务上下文(如用户ID、订单号、请求参数)对排查至关重要。SLF4J 提供了带占位符的重载方法,能安全拼接且不触发字符串拼接开销。
try {
processOrder(orderId);
} catch (ServiceException e) {
log.error("处理订单失败,orderId={}", orderId, e);
}
- 占位符
{}支持任意数量,多余参数会被忽略,缺失则留空 - 异常对象必须放在**最后一个参数**,否则不会被识别为
Throwable - 避免在日志语句中调用可能抛异常的方法(如
e.getCause().getMessage()),会导致日志失败
log.error() 和 log.error("{}", e) 有啥区别?
前者是标准用法,后者是常见误用——把异常当普通对象传给占位符,导致堆栈被 toString() 化,失去可读性。
-
log.error("操作失败", e)→ 正确:堆栈完整、可展开、支持日志系统结构化解析 -
log.error("操作失败: {}", e)→ 错误:仅输出e.toString(),例如java.lang.NullPointerException,无行号、无调用链 - 某些旧版 Log4j1 在传入
Throwable时若未用专用签名,还会抛ClassCastException
生产环境要注意的异常日志细节
堆栈太长、敏感字段泄露、日志级别错配,都可能让日志变成运维负担甚至安全风险。
立即学习“Java免费学习笔记(深入)”;
- 避免在
INFO或DEBUG级别记录完整异常(尤其含密码、token、身份证号的堆栈),应降级到ERROR并脱敏关键字段 - 异步日志(如 Logback 的
AsyncAppender)可能丢失异常上下文线程名,需开启includeCallerData="true"(但有性能代价) - Spring Boot 项目默认使用 Logback,
application.properties中加logging.exception-conversion-word=%xEx可自定义堆栈格式










