java中正确集成try-catch-finally与日志框架的关键是确保异常信息可定位、可追溯、可分级处理:需用日志框架异常重载方法记录完整堆栈和业务上下文,敏感字段脱敏,finally仅做确定性资源清理,按异常类型分层设日志级别,并通过mdc注入traceid实现链路追踪。

Java中正确集成try-catch-finally与日志框架,关键不是“包一层log.info”,而是让异常信息可定位、可追溯、可分级处理。日志不是记录发生了什么,而是帮人快速判断“现在该看哪一行代码、该查哪个服务、该联系谁”。
在catch中记录异常的完整上下文
只打印e.getMessage()或e.toString()会丢失堆栈和业务上下文。应使用日志框架的异常重载方法,并补充关键业务变量。
- 用logger.error("订单支付失败,orderNo={}", orderNo, e) —— 第三个参数传Throwable,Logback/Log4j会自动打印完整堆栈
- 避免logger.error("错误:" + e.getMessage()),字符串拼接既丢堆栈又可能空指针
- 敏感字段(如用户手机号、银行卡号)需脱敏后再写入日志,可用工具类统一处理
finally里不写业务逻辑,只做确定性清理
finally用于释放资源(如关闭流、归还连接),但绝不应在其中调用可能抛异常的业务方法,否则会掩盖原始异常。
- 数据库连接、文件流、HTTP客户端等必须在finally中close(),推荐用try-with-resources替代手动finally
- 不要在finally里调用logger.info("资源已释放")——若此时JVM正OOM或线程中断,日志可能根本写不出
- 若清理操作本身可能失败(如Socket.close()抛IOException),应单独捕获并warn记录,不throw也不影响主异常传播
按异常类型分层记录,避免日志淹没
不同异常代表不同问题等级:参数校验失败是warn,数据库连接超时是error,NPE是fatal。日志级别要与异常语义匹配。
- 业务异常(如OrderNotExistException)用warn,说明是预期内的流程分支,非系统故障
- 系统异常(如SQLException、TimeoutException)用error,需监控告警
- 未被捕获的RuntimeException建议全局配置UncaughtExceptionHandler,统一error记录+上报
- 避免所有catch都用error——大量warn级日志反而会让真正的问题被忽略
结合MDC实现链路追踪基础能力
单次请求跨多个方法或线程时,靠日志时间戳难以关联。MDC(Mapped Diagnostic Context)能给每条日志注入唯一traceId。
- 在入口(如Spring Controller)生成traceId并放入MDC:MDC.put("traceId", UUID.randomUUID().toString())
- 确保线程池执行任务时传递MDC(用ThreadPoolTaskExecutor或自定义Runnable包装)
- 日志配置中加入%X{traceId},使每行日志自动带标识,排查时grep traceId即可串联全流程
- 注意MDC是ThreadLocal,子线程需手动继承,异步场景下易丢失
不复杂但容易忽略:日志不是越详细越好,而是让看到的人3秒内知道“错在哪、严重吗、下一步做什么”。把try-catch当作日志的触发开关,而不是兜底补丁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











