1. Use AOP aspect to complete global log in springboot

1. Use AOP aspect to complete global log in springboot

You can choose to output logs on the console or collect log information and store them in the database

1. Before configuring the AOP aspect, we need to understand the functions of aspectjrelated annotations:

  • @Aspect : The function is to identify the current class as an aspect for the container to read
  • @Pointcut : (Which methods or classes need AOP weaving) define a cut point, followed by an expression, the expression can be defined as a method under a certain package, or a custom annotation, etc.;
  • After the cut point is defined, it is to make a fuss around this cut point:
    • @Before : Weave relevant code before the pointcut;
    • @After : After the pointcut, weave the relevant code;
    • @AfterReturning : After the pointcut returns the content, weave the relevant code, which is generally used in the scene where the return value is processed;
    • @AfterThrowing : Used to handle the logic processing after the woven code throws an exception;
    • @Around : Weave code before and after the pointcut, and freely control when the pointcut is executed;

2. Import dependencies

Note: You can choose not to use the second dependency. The essence is to JSONize the data and use Ali’s (you can change it if you make a mistake later)

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

        <!-- 用于日志切面中,以 json 格式打印出入参(本来使用阿里的 FASTJSON, 但是对于文件上传的接口,打印参数会报错,换为 Gson) -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

3. Declaration in the configuration file

Note: You don’t need to declare, it is enabled by default and JDK is used (that is, the dynamic proxy of the interface method, if you need to use the dynamic proxy of the class, you need to set it to true, the default is false proxy-target-class: true)

In fact, the following configuration is not required here, just to mention

#spring配置
spring:
  #切面启用
  aop:
    proxy-target-class: true
    auto: true

4. Write global log processing

Define a class with the class name: WebLogAspect(optional)

Note: The following is just to print the log information on the console. We can add an insert method at the end to insert all the information of this harvest into the log table.

At the same time, it is best to only insert additions, deletions and changes, because it involves changes to data, the query will not change the data and can be ignored (otherwise there are too many log information)

Secondly, this implements only the method of the AOP class control layer, and other layers can also do the same.

package com.zou.config;


import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class WebLogAspect {
    
    

    // 获取日志类,方便直接在控制台输出统一格式的日志信息
    private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
	
    /*
    方式1:controller包下所有类中的开头带有insert的方法
    	@Pointcut("execution(public * com.zou.controller.*.insert*(..))")
    方式2:
		@Pointcut("execution(public * com.ldb.admin.controller.*.create*(..))||" +
            "execution(public * com.ldb.admin.controller.*.update*(..))||" +
            "execution(public * com.ldb.admin.controller.*.delete*(..))")
    	注:可使用 || &&
     * 1)execution(public * (..))——表示匹配所有public方法
     * 2)execution( set(..))——表示所有以“set”开头的方法
     * 3)execution( com.xyz.service.AccountService.(..))——表示匹配所有AccountService接口的方法
     * 4)execution( com.xyz.service..(..))——表示匹配service包下所有的方法
     * 5)execution(* com.xyz.service...(..))——表示匹配service包和它的子包下的方法
     */
    
    /** 以 controller 包下定义的所有请求为切入点 */
    @Pointcut("execution(public * com.zou.controller.*.*(..))")
    public void webLog() {
    
    }

    /**
     * 在切点之前织入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()") // webLog():是你@Pointcut注解的方法名
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    
    
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 打印请求相关参数
        logger.info("========================================== Start ==========================================");
        // 打印请求 url
        logger.info("URL            : {}", request.getRequestURL().toString());
        // 打印 Http method
        logger.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求的 IP
        logger.info("IP             : {}", request.getRemoteAddr());
        // 打印请求入参
        logger.info("Request Args   : {}", new Gson().toJson(joinPoint.getArgs()));

        // 获取 @ApiOperation("") 注解中的信息,此注解属于swagger的,但也能获取,其他注解可举一反三
        ApiOperation apiOperation = null;
        
        // JoinPoint:oinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.大概就是获取目标方法的一些信息
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        apiOperation = ms.getMethod().getDeclaredAnnotation(ApiOperation.class);
        if (apiOperation != null) {
    
     ;
            logger.info("操作   : {}", apiOperation.value());
        }
    }

    /**
     * 在切点之后织入
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
    
    
        logger.info("=========================================== End ===========================================");
        // 每个请求之间空一行
        logger.info("");
    }

    /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
        logger.info("Response Args  : {}", new Gson().toJson(result));
        // 执行耗时
        logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return result;
    }


}


JoinPoint

常用API
方法名	功能
Signature getSignature();	获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();	获取传入目标方法的参数对象
Object getTarget();	获取被代理的对象
Object getThis();	获取代理对象

ProceedingJoinPoint object

The ProceedingJoinPoint object is a sub-interface of JoinPoint. This object is only used in the aspect method of @Around, and
the following two methods are added.

Object proceed() throws Throwable //执行目标方法 
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法 

Guess you like

Origin blog.csdn.net/qq_43483251/article/details/128870308