使用AOP实现统一日志拦截处理

目标

由于项目是使用的是几年前的开源项目,只是实现了基础的CRUD业务功能,缺少基础设施建设,请求一个接口也没有任何日志打印,即使异常也不会有任何日志抛出,导致排查问题很困难,于是对其进行了二次改造。

开启SQL打印

在logback.xml中修改打印日志的级别
在这里插入图片描述
结果:每次请求打印了SQL,但是参数信息并没有打印,联调有问题时需要向前端要参数模拟出错情况的参数排查,于是采用了下面的方式来打印参数

接口请求处硬编码打印请求参数

在接口请求处,采用硬编码,标识是请求哪一个接口,传了什么参数,但是发现并不打印异常信息,如果想打印,必须在每个方法加上 try-catch捕获打印才行,于是使用切面统一拦截打印入参出参、接口耗时和异常信息等等
在这里插入图片描述

使用AOP切面统一处理

统一拦截处理控制层的所有方法,于是针对带有 @RestController 的所有类,都进行切面处理。
首先定义一个切面类 LogAspect ,并加上注解 @Aspect 声明这是一个切面类,并加上 @Comonent,表明将该类交由Spring管理
在这里插入图片描述
然后声明切点,指定切入范围,使用 @Pointcut,指定范围,因为是全局统一日志拦截,所以切入的范围是所有加 @RestController 注解的类。
在这里插入图片描述

注解表达式

标准的pointcut的表达式是很丰富的,但spring aop只支持其中的9种,外加Spring aop自己扩充的一种一共是10种类型的表达式,分别如下

  • execution:一般用于指定方法执行,用的最多
  • within:指定某些类型的全部方法执行,也可用来指定一个包
  • this:Spring aop是基于代理的,生成的bean也是一个代理对象,this就是这个代理对象,当这个对象可以转换为指定的类型时,对应的切入点就是它了,spring aop将生效
  • target:当被代理的对象可以转换为指定的类型时,对应的切入点就是它了,spring aop将生效
  • args:当执行的方法的参数是指定类型时生效
  • @target:当代理的目标对象拥有指定的注解时生效
  • @args:当执行的方法参数类型上拥有指定的注解时生效
  • @within:与@target类型,官方文档描述@within只需要目标对象的类或者父类上有指定的注解,则@within会生效,而@target则是必须是目标对象的类上有指定的注解
  • @annotation:当执行的方法上拥有指定注解时生效
  • bean:当调用的方法是指定的bean的方法时生效

最后指定切点,执行目标方法,执行之后打印入参、接口名、接口耗时等信息,并且将目标方法try-catch住,如果目标方法异常,则打印错误日志提示相关错误信息,以便排查错误的原因。
在这里插入图片描述

  • @Around:增强处理
  • @Before:表示在切点方法之前执行
  • @After:表示在切点方法之后执行
  • @AfterRetruning:与@After相似
  • @AfterThrowing:当切点方法抛出异常时会执行

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Component
@Slf4j
@Aspect
public class LogAspect {

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void logPointCut() {

    }

    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        Object result;
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        String requestId = MDC.get("X-Request-Id");
        long startMills = System.currentTimeMillis();
        long cost = 0;
        try {
            result = pjp.proceed();
            cost = System.currentTimeMillis() - startMills;
            log.info("请求结束!本次请求耗时:{}, rid : {} ,url: {}, method: {}, params: {},user:{}, Authorization: {},  响应结果:{}",
                    cost, requestId, request.getRequestURL().toString(),
                    request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("Authorization"),
                    JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect));
        } catch (Exception e) {
            log.error("请求异常!!!本次请求耗时:{}, rid : {}, error:{} ,url: {}, method: {}, params: {},user:{}, Authorization: {}", cost,requestId, e, request.getRequestURL().toString(),
                    request.getMethod(), pjp.getArgs(), JSON.toJSONString(request.getAttribute("user")), request.getHeader("Authorization"));
            throw e;
        }
        return result;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值