SpingBoot日志切面(使用自定义注解打印日志)

 

使用Sping AOP切面打印日志时,为了不影响之前的代码,可以不拦截全部的controller层接口,而使用时注解的形式,在相应的接口方法加上日志注解,就可以打印请求参数和请求结果信息。

 

代码如下:

1.定义切面类

  1 @Aspect
  2 @Component
  3 @Slf4j
  4 public class LogAspect {
  5 
  6     private LogModel logModel;
  7 
  8     /**
  9      * 这里我们使用注解的形式
 10      * 当然,我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
 11      * 切点表达式:   execution(...)
 12      */
 13     @Pointcut("@annotation(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
 14     public void restControllerLog() {
 15     }
 16 
 17     /**
 18      * 需要忽略的返回日志
 19      */
 20     @Pointcut("!@annotation(com.zto.poseidon.datacenter.common.annotation.IgnoreResponseLog)")
 21     public void ignoreResponseLog() {
 22     }
 23 
 24     @Before("restControllerLog()")
 25     public void exBefore(JoinPoint joinPoint) {
 26         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 27         HttpServletRequest request = attributes.getRequest();
 28         logModel = new LogModel();
 29         logModel.setUrl(request.getRequestURL().toString());
 30         logModel.setReqIp(request.getRemoteAddr());
 31         logModel.setStartTime(DateTime.now().toDate());
 32         logModel.setMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
 33         try {
 34             logModel.setParams(getRequestParamsByJoinPoint(joinPoint));
 35             log.info("--------------请求参数日志------------");
 36             log.info(" ---> 请求参数 :{}", JSON.toJSONString(logModel));
 37         } catch (Exception e) {
 38             log.info("异常信息:{}", ExceptionUtils.getStackTrace(e));
 39         }
 40     }
 41 
 42     @After("restControllerLog()")
 43     public void exAfter(JoinPoint joinPoint) {
 44         log.info(" ---> request method:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + " 执行完毕!");
 45     }
 46 
 47     @AfterReturning(returning = "result", pointcut = "restControllerLog()  && ignoreResponseLog()")
 48     public void exAfterReturning(Object result) {
 49         log.info("--------------请求结果日志------------");
 50         log.info(" ---> 请求结果 :{}", JSON.toJSONString(result));
 51     }
 52 
 53     private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
 54         //参数名
 55         String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
 56         //参数值
 57         Object[] paramValues = joinPoint.getArgs();
 58         return buildRequestParam(paramNames, paramValues);
 59     }
 60 
 61     private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
 62         Map<String, Object> requestParams = new HashMap<>();
 63         for (int i = 0; i < paramNames.length; i++) {
 64             Object value = paramValues[i];
 65             //如果是文件对象
 66             if (value instanceof MultipartFile) {
 67                 MultipartFile file = (MultipartFile) value;
 68                 //获取文件名
 69                 value = file.getOriginalFilename();
 70             }
 71             requestParams.put(paramNames[i], value);
 72         }
 73         return requestParams;
 74     }
 75 
 76     @Data
 77     public static class LogModel {
 78 
 79         private String url;
 80 
 81         private Date startTime;
 82 
 83         private Date endTime;
 84 
 85         private Long tookTime;
 86 
 87         private Object result;
 88 
 89         private String reqIp;
 90 
 91         private Boolean success = Boolean.FALSE;
 92 
 93         private String errMsg;
 94 
 95         private Object queryString;
 96 
 97         private String method;
 98 
 99         private Map<String, Object> params;
100 
101         private RuntimeException exception;
102 
103     }
104 
105 }

 

2.添加自定义日志打印注解,打印入参和出参日志

1 @Target({ElementType.METHOD, ElementType.TYPE})
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface OperationLog {
4 }

 

3.忽视请求结果日志打印注解

1 @Target({ElementType.METHOD, ElementType.TYPE})
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface IgnoreResponseLog {
4     boolean required() default true;
5 }

 

4.然后在需要打印的接口方法上添加 @OperationLog 注解就ok了

 

 自定义注解@OperationLog 注解定义了可以使用在类和方法上,但使用在类上不生效。

看官方文档解释:

 

 

 

//@Around("@annotation(自定义注解)")//自定义注解标注在方法上的方法执行aop方法
如:@Around("@annotation(org.springframework.transaction.annotation.Transactional)")

//@Around("@within(自定义注解)")//自定义注解标注在的类上;该类的所有方法(不包含子类方法)执行aop方法
如:@Around("@within(org.springframework.transaction.annotation.Transactional)")

//@Around("within(包名前缀.*)")//com.aop.within包下所有类的所有的方法都会执行(不包含子包) aop方法
如:@Around("within(com.aop.test.*)")

//@Around("within(包名前缀..*)")//com.aop.within包下所有的方法都会执行(包含子包)aop 方法
如:@Around("within(com.aop.test..*)")

//@Around("this(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,但不包含父类的方法)都会执行aop方法
如:@Around("this(com.aop.service.TestService)")

//@Around("target(java类或接口)")//实现了该接口的类、继承该类、该类本身的类---的所有方法(包括不是接口定义的方法,包含父类的方法)
如:@Around("this(com.aop.service.TestService)")

//@Around("@target(自定义注解)")//springboot项目启动报如下错误,没有解决
// Caused by: java.lang.IllegalStateException:
// StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start

 

所以,如果想在类上使用自定义注解,需要将 LogAspect.class 切面类里的

1 @Pointcut("@annotation(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
2     public void restControllerLog() {
3     }

改为:

1 @Pointcut("@within(com.zto.poseidon.datacenter.common.annotation.OperationLog)")
2     public void restControllerLog() {
3     }

 

这样就可以使用在类上了,但是我经过测试发现,改了之后只能用在类上,用在方法上就失效了,不能正常打印日志。暂时没找到原因,如果有找到原因的朋友,可以留言评论一下。

 

posted @ 2021-12-11 13:42  [浪子回头]  阅读(734)  评论(0编辑  收藏  举报