正确记录异常堆栈需将Throwable作为error()最后一个参数,配合日志配置中%ex(Logback)或%throwable{full}(Log4j2)格式符,禁用手动转字符串或printStackTrace()。

用日志框架记录异常堆栈,核心是让框架“原生接管”Throwable对象,而不是把它转成字符串塞进日志消息里。只要传对参数、配对格式,堆栈就能完整、结构化、带上下文地落盘。
必须把异常对象作为最后一个参数传给error()方法
SLF4J(以及底层的 Logback/Log4j2)只在日志方法的最后一个参数是 Throwable 类型时,才会自动展开并格式化整个堆栈(含 cause 链、suppressed 异常)。占位符里的内容不能掺和异常。
- ✅ 正确:logger.error("读取配置失败, path={}", configPath, e);
- ❌ 错误:logger.error("读取配置失败, path=" + configPath + ", error=" + e); —— 这只调用 e.toString(),堆栈全丢
- ❌ 错误:logger.error("读取配置失败, path={}, error={}", configPath, e); —— e 被当普通字符串格式化,不触发堆栈解析
确保日志输出配置支持完整堆栈展开
即使代码写对了,如果日志格式配置没启用堆栈渲染,最终日志里还是只有几行甚至一行。关键看 pattern 中是否用了正确的转换词。
- Logback:pattern 中需包含 %ex 或 %xEx,推荐用 %ex{5}(数字表示最大展开深度),避免深层嵌套异常被截断
- Log4j2:对应的是 %throwable{full},不是 %ex;混用会导致堆栈消失
- 检查 encoder 或 appender 的 pattern 是否漏掉这部分,尤其上线前要验证日志文件实际内容
别手动把堆栈转成字符串再拼进日志
有人担心“框架可能不打全”,就自己调用 ExceptionUtils.getStackTrace(e) 或 e.getStackTrace() 拼接——这反而破坏日志质量。
立即学习“Java免费学习笔记(深入)”;
- 堆栈变成单行长文本,ELK 等系统无法高亮、解析或按行统计
- 日志框架本来就会展开堆栈,手动加一次等于重复记录,浪费磁盘和解析资源
- MDC 上下文(如 traceId)、线程名、异步上下文等都会丢失,排查时缺关键链路信息
避免用 printStackTrace() 替代日志记录
e.printStackTrace() 是调试捷径,但不适合生产环境。
- 它固定输出到 System.err,路径不可控、级别不可配、无法滚动归档
- 不携带 MDC 上下文,多线程下输出混杂,查问题时找不到归属请求
- 某些严重异常(如 OutOfMemoryError)可能被 JVM 截断,而成熟日志框架会尽力捕获完整信息
- 真正需要临时观察堆栈时,优先调高日志级别或加断点,而不是补 printStackTrace()



















