Spring AOP统一日志处理

本文介绍了一种使用Spring AOP实现的方法级切面方案。该方案定义了针对@Service注解类的方法切入,并排除特定类的方法执行。通过环绕通知实现了方法执行前后的日志记录和异常处理。
  1. 开启AOP切面配置

@Configuration
@ComponentScan(basePackages = "com.spring")
@EnableAspectJAutoProxy
public class ApplicationContextConfig {

}
  1. 切面类
/**
 * Service AOP切面类
 *
 * @author 2020/10/26
 */
@Component
@Aspect
public class AopAspect {
    private static Logger logger = LoggerFactory.getLogger(AopAspect.class);

    /**
     * 切入点:com.spring.service 包及子包下的所有类的所有public方法
     */
    @Pointcut("execution(public * com.spring.service..*.*(..)))")
    public void serviceImplClass() {
    }

    /**
     * 切入点:所有的带有@Service注解的类的所有方法
     */
    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void serviceAnnotation() {
    }

    /**
     * 需要排除的切入点:IEsbHandleService类及子类下的方法不拦截
     */
    @Pointcut("!within(com.counter.wmb.IEsbHandleService+)")
    public void excludeTarget() {
    }

    @Around("excludeTarget() && (serviceImplClass() && serviceAnnotation())")
    public Object around(ProceedingJoinPoint pjp) {
        //获取目标方法
        Method targetMethod = this.getTargetMethod(pjp);
        try {
            //1.打印方法入参参数日志
            this.printInputParamsLog(pjp, targetMethod);
            //2.调用目标方法
            return pjp.proceed();
        } catch (Throwable e) {
            //3.目标方法如果抛出异常,打印异常日志
            String errorMsg = this.printErrorLog(targetMethod, e);
            return Response.failure(errorMsg);
        } finally {
            //4.执行TraceLogUtil.clear(),清除当前线程的日志traceId
            TraceLogUtil.clear();
        }
    }

    /**
     * 打印目标方法的入参参数
     * 格式:traceId|a8bb7f1a-c0d5-47f1-80c3-8b2c0f4eadb0|CountQueryService.queryCountByStrategyId(..) input params:[cloudUserId=74662521489423,strategyId=12643]
     *
     * @param pjp          切入点
     * @param targetMethod 目标方法
     */
    private void printInputParamsLog(ProceedingJoinPoint pjp, Method targetMethod) {
        //打印方法参数信息
        String logMsg = "%s input params:[%s]";
        TraceLogUtil.info(logger, String.format(logMsg, this.getSimpleTargetMethodName(targetMethod), this.getParamsStr(pjp, targetMethod)));
    }

    /**
     * 打印目标方法异常日志
     * 格式: traceId|642b2c42-b09d-436b-8dd5-7a7e84aa7b4a|CountQueryService.queryCountByStrategyId(..) error:Invalid param[strategyId=]
     *
     * @param targetMethod 目标方法
     * @param throwable    异常
     * @return String
     */
    private String printErrorLog(Method targetMethod, Throwable throwable) {
        String errorMsg = "%s error:%s";
        errorMsg = String.format(errorMsg, this.getSimpleTargetMethodName(targetMethod), throwable.getMessage());
        TraceLogUtil.error(logger, errorMsg, throwable);
        return errorMsg;
    }

    /**
     * 获取方法参数信息
     *
     * @param pjp          切入点
     * @param targetMethod 目标方法
     * @return String
     */
    private String getParamsStr(ProceedingJoinPoint pjp, Method targetMethod) {
        //创建Spring的参数名称发现对象,获取真实的参数名称
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        //通过Spring提供的工具类,从本地变量表获取真实的参数名称
        String[] parameterNames = pnd.getParameterNames(targetMethod);
        //参数值
        Object[] args = pjp.getArgs();

        List<String> paramList = new ArrayList<>();
        for (int i = 0, length = parameterNames.length; i < length; i++) {
            paramList.add(parameterNames[i] + "=" + JacksonUtil.toJSONString(args[i]));
        }
        return StringUtils.join(paramList, ",");
    }

    /**
     * 获取切入点的目标方法的目标实现方法(注意:不是接口方法)
     *
     * @param pjp 切入点
     * @return Method
     */
    private Method getTargetMethod(ProceedingJoinPoint pjp) {
        try {
            Signature signature = pjp.getSignature();
            if (!(signature instanceof MethodSignature)) {
                throw new IllegalArgumentException("Spring AOP point cut error,only can point cut in method!");
            }
            MethodSignature methodSignature = (MethodSignature) signature;
            Object target = pjp.getTarget();
            return target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    /**
     * 获取目标方法的名称(类名.方法名)
     *
     * @param targetMethod 目标方法
     * @return String
     */
    private String getSimpleTargetMethodName(Method targetMethod) {
        return targetMethod.getDeclaringClass().getSimpleName() + "." + targetMethod.getName() + "(..)";
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值