printStackTrace() 不该用于生产日志,因其输出到 System.err 且无业务上下文;应改用 logger.error("msg", e) 并配合 MDC 和结构化日志,同时注意脱敏、根因追溯与 suppressed 异常处理。

为什么 printStackTrace() 不该直接用于生产日志
它把堆栈输出到 System.err,无法被日志框架捕获、过滤或异步写入;更严重的是,它不包含上下文(如请求 ID、用户 ID),排查时无法关联业务链路。
- 用
logger.error("订单创建失败", e)替代e.printStackTrace() - 确保日志框架(如 Logback / Log4j2)已配置
%throwable或%ex格式项,否则异常堆栈不会输出 - 避免在
catch块里先printStackTrace()再logger.error()——重复记录且污染标准错误流
如何让 Exception 的完整堆栈 + 业务变量一起落库
直接拼接字符串会丢失结构化能力,后续无法按「错误码」「HTTP 状态码」等字段查询。推荐用 MDC(Mapped Diagnostic Context)注入上下文,再结合结构化日志输出。
- 在入口处(如 Spring Controller)用
MDC.put("traceId", UUID.randomUUID().toString()) - 在
catch块中使用logger.error("biz=order_create, userId={}, orderId={}", userId, orderId, e) - Logback 配置需启用
%X{traceId},例如:%d{HH:mm:ss.SSS} [%X{traceId}] %-5level %logger{36} - %msg%n%ex
getCause() 和 getSuppressed() 怎么用才不漏关键根因
嵌套异常(如 SQLException 包裹 IOException)或 try-with-resources 抑制异常,只调 e.toString() 会丢掉最底层原因。
- 用
ThrowableUtils.getRootCause(e)(Apache Commons Lang)或手写递归遍历e.getCause() - 对 Java 7+ 的 suppressed 异常,必须显式遍历:
for (Throwable suppressed : e.getSuppressed()) { logger.warn("Suppressed: {}", suppressed.toString()); } - 注意:Spring 的
@ExceptionHandler默认只处理顶层异常,若需捕获嵌套异常,需手动解包
日志中记录敏感信息的边界在哪
密码、身份证号、银行卡号一旦进日志,就等于泄露。但完全不记参数又无法复现问题——得在「可调试」和「合规」间找平衡点。
立即学习“Java免费学习笔记(深入)”;
- 禁止在日志中出现
password=xxx、idCard=110101...等原始值;可用password=[REDACTED]占位 - 用 AOP 或自定义
Logger包装器,在log.error()前扫描参数 Map / JSON 字符串,自动脱敏指定 key -
开发环境可开全量参数日志,生产环境必须关闭
log.debug("request body: {}", json)类语句
异常链深度超过 5 层、MDC 未及时 clear() 导致线程复用污染、以及 suppress 异常被静默忽略——这三类问题在线上最难定位,也最容易在日志里留下“断头案”。










