当前位置: 首页 > news >正文

推敲见文章:从 `try..catch` 看异常日志打印的正确姿势

一、一个常见的异常处理场景

在 Java 开发中,异常处理和日志记录是基础但容易出错的环节。最近在一次代码评审中,发现了下面这段典型的异常处理代码(其中的BizException是一个自定义异常):

try { // 业务逻辑:上传身份证图片 // ... } catch (Exception e) { log.info("*****上传图片异常 reqId:{},", reqId, e); if (e instanceof BizException) { throw (BizException) e; } throw BizException.build(BossKgConstant.ERROR_6000, "身份证图片上传异常:" + ExceptionUtils.getMessage(e)); }

这段代码存在几个可优化点。下面我们通过完整的代码评审过程,探讨异常日志记录的正确做法。

二、第一次优化:修正日志级别和冗余信息

代码评审指出了两个问题:

  1. 日志级别不当:在 catch 块中使用INFO级别记录异常是不恰当的。异常通常表示错误或非预期情况,应使用ERRORWARN级别。
  2. 冗余上下文:异常堆栈通常已包含调用链信息,额外打印reqId可能非必需,尤其在高频接口中会增加日志体积。

开发者修改后的版本如下:

log.error("*****上传图片异常:{},", e);

三、第二次优化:修正日志格式问题

修改后的代码在格式上仍不够清晰:

log.error("*****上传图片异常:{},", e);

这里的格式字符串包含占位符{},但实际传入了异常对象e。在主流日志框架(如 Logback、Log4j2)中,这样写虽然能正常输出堆栈,但格式上易产生混淆。更清晰的写法是:

log.error("*****上传图片异常:", e);

四、第三次“优化”:平衡存储成本与排查效率

接下来,评审人从日常工作中强调的成本意识的角度提出:异常堆栈通常较长,全量打印会增加日志存储开销。

开发者修改后的版本如下:

} catch (Exception e) { log.error("*****上传图片异常:{}", ExceptionUtils.getMessage(e)); ... }

评审人:这一修改虽然降低了日志体积,但直接导致了堆栈跟踪信息的丢失,在后续排查问题时,只能看到异常消息,无法定位具体代码位置、调用链路和嵌套异常,显著增加了问题排查的难度。

五、哭笑不得:开发者回退到上一版本

开发者意识到“仅打印消息”会导致堆栈信息丢失后,没了主意。面对“要存储成本”和“要排查信息”这两个看似矛盾的要求,他采取了最简单的做法:直接回退到上一个“正确”的版本

log.error("*****上传图片异常:", e);

这个“回退”动作本身,恰恰暴露了问题

  1. 思考中断:开发者没有继续深入分析评审人提出的“成本”关切背后的合理成分(即:是否所有异常都需要完整堆栈?),而是选择退回到一个“安全”但未经深思的旧方案。
  2. 方案摇摆:代码在“完整堆栈”和“仅消息”两个极端之间摇摆,说明开发者尚未建立起处理这类权衡问题的稳定思路和决策框架
  3. 问题依旧:“成本”问题被搁置了,但并未被解决。如果未来日志量真的成为瓶颈,这个“回退”只是将问题推迟,而非化解。

六、正确的做法:区分异常类型,差异化处理

简单地回退或“一刀切”都是不行的。关键在于建立清晰的决策逻辑:区分异常类型,差异化处理。这能将“降低存储成本”和“保留排查线索”这两个目标统一起来。

异常类型特点日志策略建议解决的核心矛盾
自定义业务异常系统内定义,预期内,表示明确的业务规则违反(如“参数无效”、“余额不足”)。堆栈价值低。通常直接抛出,无需记ERROR日志(可记WARN)。显著降低成本:这类异常往往高频,不打印其堆栈能大幅减少日志量。
系统/运行时异常NPEIO异常DB连接异常RPC超时等。堆栈至关重要,必须记ERROR级别日志及完整堆栈。保障可排查性:这类异常是线上问题的主要来源,堆栈是定位根因的生命线。

最终的实现方案如下

try { // 业务逻辑 // ... } catch (Exception e) { if (e instanceof BizException) { // 1. 业务异常:低成本处理 // 直接抛出,通常无需记录ERROR日志。若需跟踪,可记WARN且仅记消息。 // log.warn("业务异常[code:{}]: {}", ((BizException)e).getCode(), e.getMessage()); throw (BizException) e; } // 2. 系统/运行时异常:高价值信息保留 // 必须记录ERROR和完整堆栈,这是付出的必要“成本”。 log.error("*****上传图片异常", e); // 3. 统一对外暴露 // 将系统异常转换为对上游友好的业务异常。 throw BizException.build(BossKgConstant.ERROR_6000, "身份证图片上传异常"); }

工程思维的体现
这个方案的成功之处在于,它没有在“成本”和“信息”之间二选一,而是通过分类找到了平衡点:

  • 对高频、低信息价值的业务异常,做减法,实现成本优化。
  • 对低频、高信息价值的系统异常,做加法,保障运维能力。
    这才是对“成本意识”的完整理解——成本不仅仅是存储开销,更包括潜在的故障排查时间成本。后者往往比前者高得多。

七、异常日志记录最佳实践总结

1. 合理选择日志级别

  • ERROR:用于系统异常、不可恢复错误、第三方服务调用失败。
  • WARN:用于可恢复异常、业务预期内但不希望发生的情况(如重试、降级)。
  • INFO/DEBUG:用于业务流程记录、调试信息,一般不用于记录异常本身。

2. 区分异常类型,差异化处理

try { // ... } catch (BusinessException e) { // 业务异常:可记录 WARN,通常直接抛出 log.warn("业务处理失败, code:{}, msg:{}", e.getCode(), e.getMessage()); throw e; } catch (IOException | TimeoutException e) { // 特定的系统异常:记录 ERROR 和堆栈,可附加上下文 log.error("IO操作失败 - 目标资源:{}", resourceId, e); throw new BusinessException("SYS_ERROR", "系统繁忙,请重试", e); } catch (Exception e) { // 未知异常:必须记录 ERROR 和完整堆栈 log.error("未捕获的异常", e); throw new BusinessException("SYS_ERROR", "系统内部错误"); }

3. 平衡存储成本与排查效率

  • 对高频且稳定的业务异常,可仅记录消息,或采用采样日志。
  • 对系统异常、底层中间件异常、网络超时等,必须保留完整堆栈

八、总结

一次看似简单的catch块日志记录,背后涉及了日志级别、格式规范、存储成本、排查效率、异常分类处理等多个工程权衡点。通过这次代码评审我们认识到:

  1. 避免机械执行与简单回退:理解每处修改的真实影响背后原因,建立自己的决策框架,而不是在几个选项间盲目摇摆。
  2. 坚持分类处理原则:通过区分业务异常与系统异常,能从根本上统一“降低成本”和“保留信息”这两个目标。
  3. 建立全局成本观:日志存储成本远低于因信息缺失导致的问题排查时间成本系统稳定性风险。好的设计正是在多重约束中寻找最优解。

【碎碎念一番】良好的异常日志实践,是构建可观测、易维护的系统的基石。在每次编写try-catch时,多花几秒钟思考如何记录异常,就是在为未来的自己和团队节省大量的排查时间。

http://www.rkmt.cn/news/1498035.html

相关文章:

  • 2026爱心商务卡回收哪家强?回收平台实力盘点值得收藏 - 猎卡回收公众号
  • 铁板鸭配方哪里学?详解成本核算与风味控制 - 品牌2026
  • 2026济南装修设计工作室口碑榜:五家值得关注的本地选择 - GrowthUME
  • 2026常州黄金变现全流程指南|前置准备、现场交易、售后保障一站式攻略 - 奢侈品回收测评
  • 从USACO黄油题到真实物流选址:用Dijkstra堆优化搞定最短路径加权和
  • 佛山家电维修平台推荐:本地用户反馈较多的几家服务商(2026最新发布) - 欧米到家
  • 2026东莞工厂厂房,酒店拆除回收专业公司 - 广东再生资源回收
  • 深度解析抗震支架厂家:核心知识、选型要点与工程实践 - 资讯纵览
  • 深圳搬家服务全场景搬迁指南:2026个人/家庭/企业搬迁TOP5正规靠谱机构优选 - 从来都是英雄出少年
  • 2026年成都律师事务所终极选型指南:诉讼与非诉法律服务深度横评 - 优质企业观察收录
  • 2026 年 6 月最新 | 杭州五恒系统安装公司推荐 本地暖通工程优质施工服务商甄选 - 商业新知
  • 甄选优质设备:2026真空上料与集中供料系统十大品牌汇总 - 品研笔录
  • 2026年油莎豆加工成套设备深度选型:如何为你的加工项目匹配最佳方案? - 热点速览
  • 亨得利手表保养一次多少钱?2026年劳力士/欧米茄/浪琴等9大品牌官方报价全公开 - 亨得利腕表维修中心
  • 长沙奢侈品回收2026市场调研:从闲置名包到珍藏腕表,本地靠谱渠道全解析 - 薛定谔的梨花猫
  • 2026深圳黄金回收避坑红榜,TOP5正规门店,远离套路陷阱 - 奢侈品回收测评
  • 2026年青岛搬家、家具拆装、宠物搬运物流公司精选:资质合规与服务稳妥兼具的本地搬迁供应商选择指南 - 海棠依旧大
  • 2026 武汉品牌钻饰首饰回收盘点,同城上门服务横向测评 - 奢侈品交易观察员
  • 事务详解|ACID 四大特性,搞懂数据一致性的核心
  • 别再装虚拟机了!Windows 一键拥有 Linux 环境,并跑通你的第一个视觉大模型
  • 2026年沈阳营销策划公司推荐:专业品牌营销服务商盘点
  • 2026 建瓯厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • 离散数学整理
  • 【三分钟看懂】本地生活投放朋友圈广告,如何高转化、低成本?
  • MATLAB版随机森林分类工具包:含C4.5树训练、多模型投票与结果统计分析
  • 颠覆传统!AIGC为芭比裤营销迎来视觉革命!
  • MATLAB BP神经网络隐含层节点自动试探与多种训练算法效果对比
  • 2026年6月哈尔滨黄金回收“性价比”排行榜:六家机构谁最划算? - 薛定谔的梨花猫
  • 2026 失重秤选型指南:技术参数、工况规范与主流厂家深度测评 - 品研笔录
  • 【2027最新】基于SpringBoot+Vue的搭建疫情管理系统管理系统源码+MyBatis+MySQL