关键是要将Throwable对象作为最后一个参数传给logger.error(),而非拼接字符串;正确写法为logger.error("描述", e),错误写法如logger.error("描述: " + e.getMessage())会丢失堆栈。

在 Java 中,要让日志打印出完整的调用堆栈(即异常的完整 stack trace),关键不是手动拼接字符串,而是把 Throwable 对象本身传给日志方法,而不是只传它的 getMessage() 或 toString()。
用 logger.error() 直接传异常对象
绝大多数日志框架(如 Logback、Log4j2、SLF4J + Logback)都支持在记录 error/warn 级别日志时,接收一个 Throwable 作为最后一个参数。日志框架会自动将其格式化为完整堆栈跟踪。
- ✅ 正确写法(打印完整堆栈):
这样输出会包含异常类型、消息,以及从抛出点开始逐层向上的完整方法调用链。
- ❌ 错误写法(只打一行,无堆栈):
logger.error("数据库查询失败", e.toString());
logger.error("数据库查询失败", Arrays.toString(e.getStackTrace()));
这些方式都丢失了堆栈的层级结构、类名行号、嵌套异常等关键信息,调试时基本没用。
立即学习“Java免费学习笔记(深入)”;
确保日志配置支持堆栈输出
即使代码传了异常,如果日志框架的输出模板(pattern)或 appender 配置不当,也可能截断堆栈。常见检查点:
- 使用
%ex、%xEx或%throwable(不同框架写法略有差异)在 pattern 中显式启用堆栈输出; - 避免在 pattern 中只写
%m(仅消息)而漏掉异常字段; - Logback 示例 pattern:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%ex%n; - 确认日志级别设为 ERROR 或 WARN(INFO 及以下默认不输出异常堆栈,除非显式配置)。
捕获后重新抛出时保留原始堆栈
如果在 catch 块中处理完想再往上抛,注意别用 throw new RuntimeException(e.getMessage()) 这种方式——它会丢失原始堆栈。应使用:
-
throw new RuntimeException(e);(把原异常作为 cause) - 或更推荐:直接
throw e;(不改变异常) - 若需增强信息,用带 cause 的构造函数:
throw new ServiceException("订单创建失败", e);
这样日志中最终打印的堆栈仍能追溯到最初出问题的位置。
非异常场景下主动打印当前堆栈
有时只是想看「此刻」的调用路径(比如排查诡异的执行流程),可临时打印当前线程堆栈:
logger.debug("当前调用栈", new Throwable());- 注意:这会产生一个无异常语义的
Throwable,但日志框架仍会渲染其堆栈; - 仅用于调试,上线前务必删除,避免性能开销和日志污染。



















