
直接打印 e.printStackTrace() 只会输出到标准错误流,且无法被日志框架捕获和统一管理——你看到的“堆栈”大概率没进日志文件,也搜不到、归不了档。
用 logger.error(String, Throwable) 才能完整落盘
Logback、Log4j2 等主流日志框架都支持带异常参数的重载方法。传入 Throwable 对象,框架会自动展开整个堆栈(包括 cause 链),并按配置格式写入日志文件或输出目标。
- ✅ 正确写法:
logger.error("订单处理失败,用户ID={}", userId, e); - ❌ 错误写法:
logger.error("订单处理失败,用户ID=" + userId + ", 异常=" + e);—— 这只会调用e.toString(),丢掉堆栈 - ⚠️ 注意:占位符必须在消息字符串里,异常对象必须作为最后一个参数传入,否则框架识别不了
别手动调用 ExceptionUtils.getStackTrace()(Apache Commons)
有人为“保险起见”把异常转成字符串再塞进日志,比如用 ExceptionUtils.getStackTrace(e) 拼进 message。这看似完整,实则埋了三个坑:
- 堆栈变成一行长文本,可读性差,日志系统(如 ELK)难以解析和高亮
- 重复记录:如果日志框架本身已展开堆栈,再手动加一次,等于存两份冗余内容
- 丢失上下文:原生
logger.error(msg, t)能保留 MDC 上下文(如 traceId)、异步线程名等,而字符串拼接会丢掉这些
检查日志配置是否截断堆栈(尤其 Logback 的 %ex)
即使代码写对了,日志也可能只打几行——常见于 Logback 的 PatternLayout 配置中用了不带深度参数的 %ex。
立即学习“Java免费学习笔记(深入)”;
- 默认
%ex只展开第一层异常,深层 cause(比如SQLException套着IOException)会被省略 - ✅ 应该用:
%ex{full}或%ex{5}(数字表示最大展开深度) - ⚠️ 注意:Log4j2 对应的是
%throwable{full},不是%ex;混用配置会导致堆栈消失 - 验证方法:故意抛一个嵌套异常(如
new RuntimeException("A", new IOException("B"))),看日志里是否出现 “Caused by:” 行
堆栈能不能完整落日志,关键不在“会不会写”,而在“有没有让框架真正接管异常对象”。漏掉最后一个参数、配错 pattern、或者中间多绕一手字符串转换,都会让排查时卡在“知道出错了,但不知道在哪错的”。











