在Java中如何记录异常日志_Java异常信息追踪解析

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 默认只处理顶层异常,若需捕获嵌套异常,需手动解包

日志中记录敏感信息的边界在哪

密码、身份证号、银行卡号一旦进日志,就等于泄露。但完全不记参数又无法复现问题——得在「可调试」和「合规」间找平衡点。

  • 禁止在日志中出现 password=xxxidCard=110101... 等原始值;可用 password=[REDACTED] 占位
  • 用 AOP 或自定义 Logger 包装器,在 log.error() 前扫描参数 Map / JSON 字符串,自动脱敏指定 key
  • 开发环境可开全量参数日志,生产环境必须关闭 log.debug("request body: {}", json) 类语句

异常链深度超过 5 层、MDC 未及时 clear() 导致线程复用污染、以及 suppress 异常被静默忽略——这三类问题在线上最难定位,也最容易在日志里留下“断头案”。