
Java异常告警该用拦截器还是AOP?
用 @ControllerAdvice + @ExceptionHandler 是最轻量、最可控的选择;拦截器(HandlerInterceptor)只能捕获到进入 DispatcherServlet 后的异常,漏掉参数解析失败、400 错误、甚至部分 RuntimeException;而 AOP 的 @Around 切 Exception 容易误切非业务异常(如框架内部重试抛的异常),还可能干扰事务传播。
- 优先写一个继承
ResponseEntityExceptionHandler的@ControllerAdvice类,覆盖handleException和handleMethodArgumentNotValid - 在
@ExceptionHandler方法里做两件事:记录带堆栈的告警日志、调用通知服务(比如发钉钉/企业微信) - 别在拦截器的
afterCompletion里 try-catch —— 此时异常已被处理,exception参数为 null 是常态
告警触发条件怎么设才不吵人?
直接对每个 Exception 都发告警,等于给自己装了个闹钟炸弹。真正要告警的是「未预期的、影响可用性的、重复发生的」异常。
- 白名单机制:只对特定子类告警,比如
NullPointerException、SQLException、自定义的ServiceUnavailableException;跳过IllegalArgumentException、HttpRequestMethodNotSupportedException这类客户端错误 - 加简单去重:用异常类名 + 方法签名(
joinPoint.getSignature().toShortString())作 key,5 分钟内相同 key 只发一次 - 避免在日志异步线程里发通知——Spring Boot 默认的
logging.pattern.console不会打印异步上下文中的 MDC,告警里看不到 traceId
通知服务怎么集成才不拖慢主流程?
同步调用钉钉 Webhook 或邮件 SMTP,一旦网络抖动或对方限流,你的接口响应时间直接从 200ms 涨到 5s+,还可能引发线程池耗尽。
- 必须异步:用
@Async+ 独立线程池(别共用taskExecutor),并设置拒绝策略为CALLER_RUNS防止堆积 - 加降级:通知失败时,至少把结构化告警数据写进本地文件(如
/var/log/app/alerts.log),后续用 Filebeat 拉走 - 别传完整堆栈进通知内容——HTTP body 超长容易被网关截断;只传
e.getClass().getSimpleName()、e.getMessage()、traceId、发生时间、URL 和前 3 行关键堆栈
Spring Boot 3.x 下 @ControllerAdvice 告警失效?
不是失效,是默认异常处理器变了:ResponseEntityExceptionHandler 在 Spring Boot 3 中已迁移到 org.springframework.web.servlet.mvc.method.annotation 包下,且部分方法签名调整(比如 handleHttpMessageNotReadable 返回类型从 ResponseEntity 变成 Object)。
立即学习“Java免费学习笔记(深入)”;
- 确认你继承的是新包路径下的
ResponseEntityExceptionHandler,IDE 里按住 Ctrl 点进去看包名 - 如果用了
spring-boot-starter-validation,@Valid失败不再走handleException,必须显式重写handleMethodArgumentNotValid - WebMvcConfigurer 中注册的
WebMvcConfigurationSupport子类若重写了configureHandlerExceptionResolvers,可能覆盖掉你的@ControllerAdvice,检查是否多了一层配置
告警系统最难的从来不是“怎么发”,而是“什么时候不该发”和“发完怎么查”。留好 traceId、限制通知频次、隔离通知线程池——这三件事没做扎实,告警越全,噪音越大。










