java异常日志需用logger.error("msg", e)且e置末位,slf4j才自动展开完整堆栈;logback配置须用%ex{full},禁用字符串拼接堆栈,结合mdc注入traceid并脱敏业务字段。

Java 异常日志要完整记录错误堆栈,核心是让日志框架真正“接管”异常对象,而不是把它转成字符串或绕过日志系统。关键不在写不写堆栈,而在怎么传、怎么配、怎么查。
必须用 logger.error("描述", e) 且 e 放最后
SLF4J(搭配 Logback 或 Log4j2)只在 Throwable 类型参数作为最后一个参数 时,才会自动展开完整堆栈(含 cause 链、suppressed 异常)。例如:
- ✅ 正确:
logger.error("订单支付失败, orderId={}", orderId, e); - ❌ 错误:
logger.error("订单支付失败, orderId=" + orderId + ", " + e);→ 只调用e.toString(),堆栈全丢 - ❌ 错误:
logger.error("支付失败", e.getMessage());→ 把字符串当异常传,无堆栈 - ❌ 错误:
logger.error(null, e);→ SLF4J 会 fallback 成 message 拼接,堆栈消失
检查日志配置是否支持完整展开
即使代码写对了,日志也可能只打几行——常见于格式化器没配全:
- Logback:PatternLayout 中必须用
%ex{full}或%xEx{full},不能只写%ex(默认只展开第一层) - Log4j2:对应的是
%throwable{full},不是%ex;混用会导致堆栈不显示 - 验证方法:手动抛嵌套异常,如
new RuntimeException("A", new IOException("B")),看日志里是否有Caused by:行
别手动拼堆栈字符串
用 ExceptionUtils.getStackTrace(e) 或 e.getStackTrace() 自己转字符串再塞进日志,看似“保险”,实则有害:
- 堆栈变成一行长文本,ELK 等工具无法解析、高亮、按行号检索
- 日志框架本已自动展开堆栈,再手动加一次,等于重复落盘、浪费磁盘和解析资源
- MDC 上下文(如 traceId、用户ID)在字符串拼接中丢失,链路追踪断裂
补充上下文但不泄露敏感信息
完整堆栈只是基础,排查效率取决于上下文:
- 在 error 日志中带入关键业务变量,如
userId、orderId、url,但需脱敏(如手机号掩码为138****1234) - 用 MDC 注入请求唯一标识:
MDC.put("traceId", MDC.get("traceId") != null ? MDC.get("traceId") : UUID.randomUUID().toString()); - 避免在高频循环中打 error 日志,可聚合后上报,或加限流(如每分钟最多 5 条同类错误)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











