欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springboot+@Aspect+@Log实现切面日志打印

程序员文章站 2022-11-23 10:19:01
在接口开发过程中,我们经常需要打印入参出参的log,来定位问题,比如:logger.info("xx接口返回结果{}", JSON.toJSONString(result));有时我们想要知道调用的ip地址,调用方法名等信息,那么这样一行代码就不能满足我们的要求。基于springboot项目,我们可以使用@Aspect注解声明一个日志切面,在此切面中完成统一的返回日志打印或异常日志打印,下面详细介绍:首先我们自定义一个注解Log,注解中可以定义一些属性,比如title接口注释/** * 自定义...

在接口开发过程中,我们经常需要打印入参出参的log,来定位问题,比如:

logger.info("xx接口返回结果{}", JSON.toJSONString(result));

有时我们想要知道调用的ip地址,调用方法名等信息,那么这样一行代码就不能满足我们的要求。

基于springboot项目,我们可以使用@Aspect注解声明一个日志切面,在此切面中完成统一的返回日志打印或异常日志打印,下面详细介绍:

1.首先我们自定义一个注解Log,注解中可以定义一些属性,比如title接口注释

/**
 * 自定义操作日志记录注解
 *
 * @author china
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 接口注释
     */
    String title() default "";
}

2.在接口中使用@Log的注解来开启,注意注解只能作用在方法上

@Log(title = "切面测试接口")
@GetMapping()
 public String hello() {
     for (int i = 0; i < 10; i++) {
         String traceId = UUID.randomUUID().toString();
         log.info("请求打印traceId:" + traceId);
     }
     return "日志切面接口测试成功";
 }

3.接下来我们开始介绍@Aspect注解,首先介绍下相关注解

@Aspect:作用是把当前类标识为一个切面供容器读取
 
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 publicvoid型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行

下面贴出LogAspect代码,方法logPointCut定义了一个切点,注解@PointCut中@annotation(),括号中表示上述我们定义的@Log注解的位置,当然我们也可以使用切点表达式execution(),方法doAfterReturning和doAfterThrowing是正常返回和异常返回执行的方法。

/**
 * 系统日志切面
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /**
     * 这里我们使用注解的形式
     * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     */
    @Pointcut("@annotation(com.demo.framework.aspectj.lang.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        saveLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        saveLog(joinPoint, e, null);
    }
}

4.方法saveLog为业务逻辑方法,主要是解释如何获取需要打印的内容,如下:

/**
 * 保存日志
  */
 private void saveLog(JoinPoint joinPoint, final Exception e, Object jsonResult) {
     MethodSignature signature = (MethodSignature) joinPoint.getSignature();
     Method method = signature.getMethod();
     // 请求的地址
     String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
     LogBO sysLogBO = new LogBO();
     sysLogBO.setIp(ip);
     sysLogBO.setResult(JSON.toJSONString(jsonResult));
     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     sysLogBO.setCreateDate(dateFormat.format(new Date()));
     Log sysLog = method.getAnnotation(Log.class);
     if (sysLog != null) {
         //注解上的描述
         sysLogBO.setRemark(sysLog.title());
     }
     //请求的 类名、方法名
     String className = joinPoint.getTarget().getClass().getName();
     String methodName = signature.getName();
     sysLogBO.setClassName(className);
     sysLogBO.setMethodName(methodName);
     //请求的参数
     Object[] args = joinPoint.getArgs();
     try {
         List<String> list = new ArrayList<String>();
         for (Object o : args) {
             list.add(JSON.toJSONString(o));
         }
         sysLogBO.setParams(list.toString());
     } catch (Exception ex) {
         // 记录本地异常日志
         log.error("异常信息:{}", ex.getMessage());
     }
     log.info("ip地址:{},创建时间:{},调用方法名:{},参数:{},返回值:{}",
             sysLogBO.getIp(), sysLogBO.getCreateDate(), sysLogBO.getMethodName(), sysLogBO.getParams(), sysLogBO.getResult());
 }

5.至此,代码部分结束,主要是介绍了使用@Aspect定义日志切面,最后贴出测试结果:
springboot+@Aspect+@Log实现切面日志打印参考文章:https://blog.csdn.net/zhuzhezhuzhe1

本文地址:https://blog.csdn.net/weixin_38117908/article/details/107283999