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     }

 

這樣就可以使用在類上了,但是我經過測試發現,改了之后只能用在類上,用在方法上就失效了,不能正常打印日志。暫時沒找到原因,如果有找到原因的朋友,可以留言評論一下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM