Java需手动实现自定义异常去重收集:定义含errorCode/contextId/params的异常类并重写equals/hashCode;用ConcurrentHashMap缓存指纹及统计;通过全局处理器/AOP/工具方法统一接入;建议增加scene字段、消息归一化和关键堆栈行参与指纹计算。

Java 中没有内置的“自定义异常自动去重收集”机制,需要手动设计。核心思路是:捕获异常后,不直接抛出或打印,而是交由一个中心化管理器统一处理,通过异常类型、关键字段(如错误码、消息摘要、堆栈关键行)做判重,再按需聚合、去重、上报或记录。
定义可判重的自定义异常类
让异常自身支持内容比较,便于后续去重识别:
- 继承 RuntimeException 或 Exception,根据是否需强制捕获决定
- 包含结构化字段:如
errorCode(String/Enum)、contextId(追踪ID)、params(影响判重的参数列表) - 重写 equals() 和 hashCode(),只基于判重关键字段(避免包含时间戳、全堆栈等易变内容)
- 可选:提供 toFingerprint() 方法,返回标准化字符串(如
errorCode + "|" + contextId + "|" + sorted(params)),用于快速哈希
实现去重收集管理器
用线程安全的容器(如 ConcurrentHashMap)缓存最近出现的异常指纹及其统计信息:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 使用
ConcurrentMap<String, ExceptionRecord>,key 是指纹,value 包含首次发生时间、最近发生时间、累计次数、示例堆栈等 - 提供 record(Exception e) 方法:生成指纹 → 若已存在则计数+1、更新时间;否则新建记录
- 可加过期策略:例如用
ExpiringMap或后台线程定期清理超时(如 5 分钟)未更新的条目 - 避免内存泄漏:限制最大缓存条数(LRU 或定时淘汰)
在业务中统一接入异常收集点
不要在每个 catch 块里重复写收集逻辑,推荐以下方式之一:
立即学习“Java免费学习笔记(深入)”;
-
全局异常处理器(Spring Boot):实现
@ControllerAdvice+@ExceptionHandler,统一拦截自定义异常并调用收集器 -
AOP 切面:对指定注解(如
@TrackErrors)的方法环绕增强,在 catch 块中自动收集 -
工具方法封装:提供
ErrorCollector.log(e),业务代码显式调用(适合非 Spring 环境或局部控制)
补充建议:提升去重准确性
仅靠异常类名和消息容易误判(如 “用户不存在” 但 ID 不同),建议:
- 在构造异常时传入可标识业务场景的
scene字段(如"order_create") - 对消息做轻量归一化:正则替换动态值为占位符(如
"user_123"→"user_#") - 若堆栈关键行稳定(如固定某一行抛出),可截取该行 + 行号参与指纹计算
- 避免将整个
Throwable.printStackTrace()加入判重——内容太长且不稳定

















