springboot+aop切點記錄請求和響應資訊

神牛003發表於2018-11-07

  本篇主要分享的是springboot中結合aop方式來記錄請求引數和響應的資料資訊;這裡主要講解兩種切入點方式,一種方法切入,一種註解切入;首先建立個springboot測試工程並通過maven新增如下依賴:

        <!-- AOP -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!--阿里 FastJson依賴-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>

  先來說方法的切點方式,需要建立個名為LogAspect的元件類,然後用@Aspect註解修飾元件類,再通過設定方法切入點方式做公共日誌記錄,如下建立切入點:

    //切點入口 Controller包下面所有類的所有方法
    private final String pointcut = "execution(* com.platform.Controller..*(..))";

    //切點
    @Pointcut(value = pointcut)
    public void log() {
    }

  這裡的execution( com.platform.Controller..(..))主要的意思是:切入點入口是Controller包下面所有類的所有方法;再來通過@Around環繞註解方法裡面做請求引數和響應資訊的記錄,如下程式碼:

    @Around(value = "log()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object result = null;
        StringBuilder sbLog = new StringBuilder("\n");
        try {
            sbLog.append(String.format("類名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName()));

            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            sbLog.append(String.format("方法:%s\r\n", methodSignature.getMethod().getName()));

            Object[] args = proceedingJoinPoint.getArgs();
            for (Object o : args) {
                sbLog.append(String.format("引數:%s\r\n", JSON.toJSON(o)));
            }

            long startTime = System.currentTimeMillis();
            result = proceedingJoinPoint.proceed();
            long endTime = System.currentTimeMillis();
            sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result)));
            sbLog.append(String.format("耗時:%ss", endTime - startTime));
        } catch (Exception ex) {
            sbLog.append(String.format("異常:%s", ex.getMessage()));
        } finally {
            logger.info(sbLog.toString());
        }
        return result;
    }

  此刻主要程式碼就完成了,再來我們配置下日誌的記錄方式;首先在 resources目錄增加名為logback-spring.xml的檔案,其配置資訊如:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!--
 3   ~ Author:shenniu003
 4   ~ Copyright (c) 2018.
 5   -->
 6 
 7 <configuration debug="false" scan="true" scanPeriod="1 seconds">
 8 
 9     <springProperty scope="context" name="appname" source="logging.logback.appname"/>
10     <springProperty scope="context" name="logLevel" source="logging.logback.level"/>
11     <springProperty scope="context" name="logPath" source="logging.logback.path"/>
12 
13     <property name="logPathAll" value="${logPath}/${appname}.log"/>
14 
15     <contextName>logback</contextName>
16 
17     <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
18         <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter" >
19              <level>WARN</level>
20          </filter>-->
21         <encoder>
22             <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
23         </encoder>
24     </appender>
25 
26     <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
27         <file>${logPathAll}</file>
28         <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
29             <fileNamePattern>${logPathAll}.%d{yyyy-MM-dd}.zip</fileNamePattern>
30         </rollingPolicy>
31         <encoder>
32             <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
33             </pattern>
34         </encoder>
35     </appender>
36 
37     <root level="${logLevel}">
38         <appender-ref ref="console"/>
39         <appender-ref ref="file"/>
40     </root>
41 
42 </configuration>

  然後application.yml的配置資訊如:

1 logging:
2   config: classpath:logback-spring.xml
3   logback:
4     level: info #info ,debug
5     path: /home/app/data/applogs/weblog
6     appname: web

  此刻日誌和公共的aop記錄類都完成了,我們需要建立個測試用例,其程式碼如:

1     @PostMapping("/addUser")
2     public ResponseEntity<MoStudent> addUser(@RequestBody MoStudent moStudent) throws Exception {
3         moStudent.setNumber(UUID.randomUUID().toString());
4 //        throw new Exception("錯誤了");
5         return new ResponseEntity<>(moStudent, HttpStatus.OK);
6     }

  最後,通過postman模擬post請求,能夠得到如下的日誌結果:

  

  上面的方式切入點是所有方法,所有方法都記錄日誌可能有是不是需求想要的,此時可以通過註解的方式來標記想記錄日誌的方法;先來建立個日誌註解:

 1 @Target(ElementType.METHOD)
 2 @Retention(RetentionPolicy.RUNTIME)
 3 @Documented
 4 public @interface LogAnnotation {
 5     /**
 6      * 描述
 7      *
 8      * @return
 9      */
10     String des() default "";
11 }

  同樣再來建立註解的切入點:

1     //匹配方法上包含此註解的方法
2     private final String annotationPointCut = "@annotation(com.platform.Aop.LogAnnotation)";
3 
4     //註解切點
5     @Pointcut(value = annotationPointCut)
6     public void logAnnotation() {
7     }

  再通過@Around註解繫結具體的操作方法:

 1     @Around(value = "logAnnotation()")
 2     public Object aroundAnnotation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
 3         Object result = null;
 4         StringBuilder sbLog = new StringBuilder("\n");
 5         try {
 6             sbLog.append(String.format("類名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName()));
 7 
 8             MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
 9             Method method = methodSignature.getMethod();
10             LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
11             if (logAnnotation != null && !logAnnotation.des().isEmpty()) {
12                 sbLog.append(String.format("說明:%s\r\n", logAnnotation.des()));
13             }
14             sbLog.append(String.format("方法:%s\r\n", method.getName()));
15 
16             Object[] args = proceedingJoinPoint.getArgs();
17             for (Object o : args) {
18                 sbLog.append(String.format("引數:%s\r\n", JSON.toJSON(o)));
19             }
20 
21             long startTime = System.currentTimeMillis();
22             result = proceedingJoinPoint.proceed();
23             long endTime = System.currentTimeMillis();
24             sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result)));
25             sbLog.append(String.format("耗時:%ss", endTime - startTime));
26         } catch (Exception ex) {
27             sbLog.append(String.format("異常:%s", ex.getMessage()));
28         } finally {
29             logger.info(sbLog.toString());
30         }
31         return result;
32     }

  這個方法裡需要注意的是註解方式相比第一種其實就多瞭如下幾行程式碼:

1             Method method = methodSignature.getMethod();
2             LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
3             if (logAnnotation != null && !logAnnotation.des().isEmpty()) {
4                 sbLog.append(String.format("說明:%s\r\n", logAnnotation.des()));
5             }

  下面是註解測試用例:

1     @LogAnnotation(des = "註解記錄日誌")
2     @PostMapping("/addUser01")
3     public ResponseEntity<MoStudent> addUser01(@RequestBody MoStudent moStudent) throws Exception {
4         moStudent.setNumber(UUID.randomUUID().toString());
5         return new ResponseEntity<>(moStudent, HttpStatus.OK);
6     }

  如下是LogAspect.java的所有程式碼:

  1 /*
  2  * Author:shenniu003
  3  * Copyright (c) 2018.
  4  */
  5 
  6 package com.platform.Aop;
  7 
  8 import com.alibaba.fastjson.JSON;
  9 import org.aspectj.lang.ProceedingJoinPoint;
 10 import org.aspectj.lang.annotation.*;
 11 import org.aspectj.lang.reflect.MethodSignature;
 12 import org.slf4j.Logger;
 13 import org.slf4j.LoggerFactory;
 14 import org.springframework.stereotype.Component;
 15 
 16 import java.lang.reflect.Method;
 17 
 18 /**
 19  * Created by Administrator on 2018/11/5.
 20  * springboot+aop切點記錄請求和響應資訊
 21  */
 22 @Component
 23 @Aspect
 24 public class LogAspect {
 25 
 26     //切點入口 Controller包下面所有類的所有方法
 27     private final String pointcut = "execution(* com.platform.Controller..*(..))";
 28 
 29     //匹配方法上包含此註解的方法
 30     private final String annotationPointCut = "@annotation(com.platform.Aop.LogAnnotation)";
 31 
 32     private Logger logger = LoggerFactory.getLogger(LogAspect.class);
 33 
 34     //切點
 35     @Pointcut(value = pointcut)
 36     public void log() {
 37     }
 38 
 39     @Around(value = "log()")
 40     public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
 41         Object result = null;
 42         StringBuilder sbLog = new StringBuilder("\n");
 43         try {
 44             sbLog.append(String.format("類名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName()));
 45 
 46             MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
 47             sbLog.append(String.format("方法:%s\r\n", methodSignature.getMethod().getName()));
 48 
 49             Object[] args = proceedingJoinPoint.getArgs();
 50             for (Object o : args) {
 51                 sbLog.append(String.format("引數:%s\r\n", JSON.toJSON(o)));
 52             }
 53 
 54             long startTime = System.currentTimeMillis();
 55             result = proceedingJoinPoint.proceed();
 56             long endTime = System.currentTimeMillis();
 57             sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result)));
 58             sbLog.append(String.format("耗時:%ss", endTime - startTime));
 59         } catch (Exception ex) {
 60             sbLog.append(String.format("異常:%s", ex.getMessage()));
 61         } finally {
 62             logger.info(sbLog.toString());
 63         }
 64         return result;
 65     }
 66 
 67     //註解切點
 68     @Pointcut(value = annotationPointCut)
 69     public void logAnnotation() {
 70     }
 71 
 72     @Around(value = "logAnnotation()")
 73     public Object aroundAnnotation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
 74         Object result = null;
 75         StringBuilder sbLog = new StringBuilder("\n");
 76         try {
 77             sbLog.append(String.format("類名:%s\r\n", proceedingJoinPoint.getTarget().getClass().getName()));
 78 
 79             MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
 80             Method method = methodSignature.getMethod();
 81             LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
 82             if (logAnnotation != null && !logAnnotation.des().isEmpty()) {
 83                 sbLog.append(String.format("說明:%s\r\n", logAnnotation.des()));
 84             }
 85             sbLog.append(String.format("方法:%s\r\n", method.getName()));
 86 
 87             Object[] args = proceedingJoinPoint.getArgs();
 88             for (Object o : args) {
 89                 sbLog.append(String.format("引數:%s\r\n", JSON.toJSON(o)));
 90             }
 91 
 92             long startTime = System.currentTimeMillis();
 93             result = proceedingJoinPoint.proceed();
 94             long endTime = System.currentTimeMillis();
 95             sbLog.append(String.format("返回:%s\r\n", JSON.toJSON(result)));
 96             sbLog.append(String.format("耗時:%ss", endTime - startTime));
 97         } catch (Exception ex) {
 98             sbLog.append(String.format("異常:%s", ex.getMessage()));
 99         } finally {
100             logger.info(sbLog.toString());
101         }
102         return result;
103     }
104 }
View Code

相關文章