java异常日志打印须避免信息丢失等四大问题,应统一用slf4j记录带堆栈的error/warn日志,禁用system.out和printstacktrace,catch块必须处理、记录或补偿,日志需含上下文、脱敏、占位符,包装异常要精简message并保留cause。

Java异常日志打印的常见反模式,核心问题在于“信息丢失、定位困难、干扰排查、埋下隐患”。避开它们不靠经验直觉,而要靠明确规则和工具约束。
别用 System.out.println(e) 或 e.printStackTrace()
这两者在生产环境等于没记录:输出到控制台,无法留存、不可检索、无上下文、不支持分级。更严重的是,e.printStackTrace() 会把堆栈转成字符串写入内存,长异常可能触发字符串常量池溢出,直接卡住请求。
- ✅ 统一使用 SLF4J(如搭配 Logback),声明为
private static final Logger log = LoggerFactory.getLogger(YourClass.class); - ✅ 记录异常必须带堆栈:
log.error("订单创建失败", e),而不是log.error("订单创建失败") - ✅ 禁止在任何构建流程中允许
System.out或printStackTrace出现在业务代码里——CI 阶段用 SonarQube 规则 S1186 拦截
避免空 catch 块或仅 log.info(e)
catch 住异常却不处理、不记录完整堆栈、不抛出、不补偿,错误就彻底消失。而用 info 级别记 error,会让告警失效、日志淹没、问题静默。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- ✅ 所有
catch块必须满足至少一项:
• 调用log.error(..., e)或log.warn(..., e)(按严重性选级)
• 抛出封装后的业务异常(如throw new BusinessException("支付超时", e))
• 执行明确补偿动作,并加注释说明“此处吞异常安全,因XXX” - ✅ 在 CI 中启用 Checkstyle 的
EmptyCatchBlock规则,或 SonarQube 的 S2221,发现即阻断提交
拒绝无上下文、无脱敏、无结构的日志
只写 "操作失败" 不知道谁、哪个订单、什么时间;打印完整用户对象可能泄露身份证、手机号;拼接长字符串既低效又难解析。
- ✅ 日志消息必须含关键业务上下文:用户ID、订单号、请求ID(推荐用 MDC 注入
MDC.put("traceId", requestId)) - ✅ 敏感字段必须脱敏:记录
"userPhone=138****1234",而非原始user.toString() - ✅ 一律用占位符:
log.error("转账失败,目标账户[{}]余额不足", targetAccountNo, e),避免字符串拼接和无效计算
不层层包装异常,也不丢弃原始堆栈
每层都写 throw new RuntimeException("xxx", e) 却不清理冗余 message 或 cause 链,会导致 N 层嵌套堆栈反复出现,日志体积暴增、可读性归零。
- ✅ 包装异常时,优先复用原始异常的 message,或精简补充业务语义,避免重复描述
- ✅ 必须传递 cause:
throw new ServiceException("库存校验异常", e),不能丢掉e - ✅ 对非关键底层异常(如解析可选参数失败),改用
log.debug("忽略无效参数: {}", input),不升级为 error
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










