直接打印log.error("msg", e)能完整保留异常位置、调用链和嵌套因果关系,而e.getmessage()仅返回模糊描述,无法定位根因;日志框架递归展开cause链,配合%ex{full}或%throwable{full}配置可确保堆栈完整落盘。

直接打印 e(即 log.error("msg", e))比只记录 e.getMessage() 具有压倒性的调试价值——核心差距在于是否保留了异常发生的位置、调用链和嵌套因果关系。
堆栈信息决定能否定位根因
日志中缺失堆栈,等于拿掉导航仪开车。e.getMessage() 仅返回一句描述,比如 "Null pointer" 或 "Connection refused",但完全无法回答:哪一行代码空了?是哪个 service 方法传入了 null?上游 controller 是怎么把非法参数透过来的?而 log.error("msg", e) 自动输出完整堆栈,从最内层抛出点开始,逐层向上展示方法调用路径,让问题可追溯、可复现。
嵌套异常容易被 getMessage() 彻底掩盖
真实系统中大量异常是包装型的:比如一个 ServiceException 包着 SQLException,再底层可能是 TimeoutException。e.getMessage() 只取最外层那句模糊提示,如 "业务处理失败",真正的数据库超时原因彻底丢失。而直接传 e 给日志框架(配合 Logback 的 %xEx 或 Log4j2 的 %ex),能递归展开所有 cause,把根因暴露在日志首屏。
toString() 和拼接方式看似有信息,实则不可靠
像 log.error("msg: " + e) 表面看也带类名和消息,但它调用的是 e.toString(),不包含堆栈;更危险的是,它提前做了字符串拼接,既浪费 CPU,又可能因异常重写 toString 导致死循环或 NPE。而 log.error("msg", e) 是延迟解析,日志框架内部按需格式化,安全且高效。
生产环境也不能妥协堆栈完整性
- 不要为“日志体积大”而放弃堆栈——现代日志系统(ELK、Loki)都支持折叠/搜索堆栈,关键信息反而更容易被检索到
- 若担心敏感字段泄露,应通过脱敏过滤器处理,而不是砍掉堆栈
- 对已知包装异常,可主动用
e.getCause() != null ? e.getCause() : e或ExceptionUtils.getRootCause(e)提升根因可见性










