异常上报需兼顾信息完整性与业务稳定性:开发环境详打堆栈,生产环境脱敏写结构化日志并同步监控指标,关键链路异步告警且设超时降级;禁用高风险操作,统一入口封装,结合mdc增强上下文,配置失败兜底机制。

在catch块中上报异常,核心是既要保证错误信息不丢失,又要避免干扰主业务逻辑或引发二次异常。关键不是简单打印堆栈,而是有策略地记录、分类、传递和必要时降级处理。
明确上报目的,区分日志记录与监控告警
上报不等于“全量打印”。需先判断场景:
- 开发/测试环境:优先输出详细堆栈到控制台或本地日志,便于快速定位;
- 生产环境:避免敏感信息(如密码、用户身份证)直接落盘,应脱敏后写入结构化日志(如JSON格式),并同步触发监控指标(如exception_count);
- 关键链路(如支付回调):除记录外,还需异步通知运维群或触发告警(如通过HTTP调用企业微信机器人),但必须设置超时和失败降级(如写入本地重试队列)。
避免在catch中做高风险操作
catch块本身处于异常上下文中,稳定性差,以下操作极易导致问题:
- 禁止在catch里调用可能抛异常的外部服务(如数据库写入、远程HTTP请求)——若上报服务不可用,会掩盖原始异常;
- 避免使用阻塞式日志框架(如未配置异步Appender的Log4j2同步模式),防止线程卡死;
- 不要在catch中重新抛出未经包装的原始异常(如
throw e;),丢失了异常发生时的上下文(如入参、用户ID)。推荐封装为自定义业务异常:throw new ServiceException("订单创建失败", e);
统一异常上报入口,减少重复代码
不要在每个catch里手写日志和上报逻辑。推荐两种轻量方式:
- 封装工具方法:
ExceptionReporter.report(e, "order.create", Map.of("orderId", id, "userId", uid));,内部自动添加时间、服务名、TraceID等字段; - 结合AOP,在Controller层统一拦截
@ExceptionHandler,对特定异常类型执行标准化上报,同时返回友好提示给前端; - 使用MDC(Mapped Diagnostic Context)在线程开始时注入关键上下文(如requestId、userId),确保所有日志自动携带,无需在每个catch里手动拼接。
考虑失败兜底,保障系统可用性
上报本身可能失败(网络抖动、日志磁盘满、监控服务宕机)。必须设计降级路径:
- 优先写入内存缓冲区(如RingBuffer),由后台线程异步刷盘或重试;
- 设置上报失败阈值(如连续3次失败),触发开关关闭实时上报,转为本地文件暂存;
- 对非核心异常(如第三方头像加载失败),可仅记录warn级别日志,不触发告警,避免噪音。
异常上报不是越全越好,而是越准、越稳、越可追溯越好。一次清晰的错误上下文,比十次堆栈打印更有价值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











