SpringBoot自定義註解、AOP列印日誌

garfieldzf發表於2019-05-11

前言

       在SpringBoot中使用自定義註解、aop切面列印web請求日誌。主要是想把controller的每個request請求日誌收集起來,呼叫介面、執行時間、返回值這幾個重要的資訊儲存到資料庫裡,然後可以使用火焰圖統計介面呼叫時長,平均響應時長,以便於我們對介面的呼叫和執行情況及時掌握。

新增依賴

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
      <version>2.1.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
    </dependency>

 

自定義註解

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WebLogger {

    String value() default "";
}

  

AOP定義切面、切點

      在實現切面之前先要了解幾個aop的註解:

  •  @Aspect、
  •  @Pointcut 定義切點,後面跟隨一個表示式,表示式可以是一個註解,也可以具體到方法級別。
  •  @Before
  •  @After
  •  @Around

      實現切面,在before方法裡統計request請求相關引數,在around方法裡計算介面呼叫時間。這裡統計請求的引數時有個bug,joinpoint.getArgs返回值是一個Object陣列,但是陣列裡只有引數值,沒有引數名。還沒找到解決辦法。

@Aspect
@Component
public class WebLoggerAspect {

    @Pointcut("@annotation(com.zhangfei.anno.WebLogger)"
    public void log() {}

    @Around("log()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime=System.currentTimeMillis();

        Object result=joinPoint.proceed();
        System.out.println("Response:"+new Gson().toJson(result));
        System.out.println("耗時:"+(System.currentTimeMillis()-startTime));

        return result;
    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        System.out.println("==================Start=================");
        System.out.println("URL:" + request.getRequestURL().toString());
        System.out.println("Description:" + getLogValue(joinPoint));
        System.out.println("Method:" + request.getMethod().toString());

        //列印controller全路徑及method
        System.out.println("Class Method:" + joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
        System.out.println("客戶端IP:" + request.getRemoteAddr());

        System.out.println("請求引數:" + new Gson().toJson(joinPoint.getArgs()));

    }

    private String getLogValue(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        WebLogger webLogger = method.getAnnotation(WebLogger.class);

        return webLogger.value();
    }

    @After("log()")
    public void doAfter() {
        System.out.println("==================End=================");
    }
} 

怎麼使用註解?

      這裡就直接在你的web請求方法上直接新增WebLogger註解

@Controller
@RequestMapping("/student")
public class StudentController {

    private final Logger logger= LoggerFactory.getLogger(StudentController.class);

    @GetMapping("/list")
    @WebLogger("學生列表")
    public @ResponseBody  List<Student> list(){
        ArrayList<Student> list=new ArrayList<>();
        Student student0=new Student(1,"kobe",30);
        Student student1=new Student(2,"james",30);
        Student student2=new Student(3,"rose",30);

        list.add(student0);
        list.add(student1);
        list.add(student2);

        return list;
    }

    @WebLogger("學生實體")
    @RequestMapping("/detail")
    public @ResponseBody Student student(int id){
        return new Student(1,"kobe",30);
    }

}

 

切面日誌輸出效果

 

總結

        從頭條上一位朋友文章評論上看到有人提出,在分散式部署的環境中,出現高併發時日誌列印會出現錯亂的情況,這裡還需要把執行緒id或者宣告一個guid, 存入ThreadLoal中統計使用。當然更多的人簡歷還是使用ELK等分散式日誌解決方法,好吧,這些還有待於自己部署環境去嘗試。

相關文章