You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot基于Bean或AOP的异常处理方案选型咨询

Spring Boot异常场景下统一发送监控指标的最佳实践

核心问题拆解

需要在**所有异常发生时(包括try/catch捕获的异常和未捕获被@ControllerAdvice兜底的异常)**统一发送监控指标,避免在每个业务类中重复注入MetricsBean,同时不破坏现有代码结构。

推荐方案

方案1:Spring AOP 切面拦截日志方法(最通用)

这是你考虑的选项3,也是最符合"通用无侵入"需求的方案。通过AOP拦截org.slf4j.Logger的error方法,在日志记录前后调用MetricsBean发送指标。

实现步骤:

  1. 确保项目引入Spring AOP依赖(Spring Boot默认已包含)
  2. 编写AOP切面类:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ErrorLogMetricsAspect {

    @Autowired
    private MetricsBean metricsBean;

    // 定义切入点:拦截指定包下所有SLF4J Logger的error方法
    @Pointcut("execution(* org.slf4j.Logger.error(..)) && within(com.yourpackage..*)")
    public void errorLogPointcut() {}

    // 日志记录完成后发送指标
    @AfterReturning(pointcut = "errorLogPointcut()")
    public void afterErrorLog(JoinPoint joinPoint) {
        // 提取异常参数
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof Throwable) {
                // 根据异常类型发送对应指标
                metricsBean.recordExceptionMetric(((Throwable) arg).getClass().getSimpleName());
                break;
            }
        }
        // 针对无异常的error日志发送通用错误指标
        metricsBean.recordGeneralErrorMetric();
    }
}
  1. 注意事项:
    • 用within表达式缩小拦截范围,避免统计框架内部的错误日志
    • 区分带异常参数和不带异常参数的error方法,避免误统计
    • 确保MetricsBean的方法是线程安全的,适配多线程调用场景

方案2:封装自定义日志工具类(侵入性低,可控性强)

创建Spring管理的日志工具类,封装Logger和MetricsBean的逻辑,业务类直接使用该工具类代替原生Logger。

实现步骤:

  1. 编写日志工具类:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class LogAndMetricsUtil {

    @Autowired
    private MetricsBean metricsBean;

    public void error(Class<?> clazz, String message, Throwable throwable) {
        Logger logger = LoggerFactory.getLogger(clazz);
        logger.error(message, throwable);
        // 发送异常指标
        metricsBean.recordExceptionMetric(throwable.getClass().getSimpleName());
    }

    // 重载方法,简化无消息参数的调用
    public void error(Class<?> clazz, Throwable throwable) {
        error(clazz, throwable.getMessage(), throwable);
    }
}
  1. 业务类中注入并使用:
@Service
public class YourService {

    @Autowired
    private LogAndMetricsUtil logAndMetricsUtil;

    public void doBusiness() {
        try {
            // 业务逻辑
        } catch (Exception ex) {
            logAndMetricsUtil.error(this.getClass(), ex);
        }
    }
}
  1. 优势:
    • 完全可控,仅统计业务代码主动调用的日志
    • 可灵活扩展日志与指标的关联逻辑,比如添加自定义标签
    • 不会被框架内部日志干扰

方案3:扩展日志框架的Appender(全局统计)

如果使用Logback/Log4j2,可自定义Appender,在日志输出时触发指标发送,无需Spring AOP,直接通过日志框架扩展实现。

Logback示例:

  1. 编写自定义Appender:
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MetricsAppender extends AppenderBase<ILoggingEvent> {

    @Autowired
    private MetricsBean metricsBean;

    @Override
    protected void append(ILoggingEvent event) {
        // 仅处理ERROR级别日志
        if (event.getLevel().isGreaterOrEqual(ch.qos.logback.classic.Level.ERROR)) {
            Throwable throwable = event.getThrowableProxy() != null 
                ? event.getThrowableProxy().getThrowable() 
                : null;
            if (throwable != null) {
                metricsBean.recordExceptionMetric(throwable.getClass().getSimpleName());
            } else {
                metricsBean.recordGeneralErrorMetric();
            }
        }
    }
}
  1. 在logback-spring.xml中配置:
<configuration>
    <appender name="METRICS" class="com.yourpackage.MetricsAppender"/>
    <root level="INFO">
        <appender-ref ref="METRICS"/>
        <!-- 其他已配置的appender -->
    </root>
</configuration>
  1. 优势:
    • 完全无侵入,无需修改业务代码
    • 覆盖所有日志框架输出的ERROR日志,包括框架内部异常

方案对比与选择

方案侵入性可控性适用场景
Spring AOP低中需要拦截特定包/类的日志,或与Spring上下文深度结合
自定义日志工具类中(需替换业务类的Logger)高仅统计业务代码主动捕获的异常,需要灵活扩展逻辑
日志框架Appender无低全局统计所有ERROR级日志,包括框架异常

补充说明

  • @ControllerAdvice中的兜底异常已可直接使用MetricsBean,需注意避免与上述方案重复统计(比如在AOP/Appender中排除该类的日志,或不在@ControllerAdvice中记录ERROR日志)

内容的提问来源于stack exchange,提问作者hitchhiker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 00:15:38