SpringBoot Integrates AOP Recording Interface Access Logs

Posted by qbox on Fri, 13 Mar 2020 03:40:43 +0100

Reference resources:

SpringBoot Integration AOP

AOP

AOP is the abbreviation of Aspect Oriented Programming, which means: facet-oriented programming, a technology for unified maintenance of program functions through precompilation and run-time dynamic agents.With AOP, individual parts of business logic can be isolated, which reduces the coupling between parts of business logic, improves the reusability of programs, and improves the efficiency of development.

Terminology related to AOP

Advice

The notification describes the work to be done by the facet and when it will be executed.For example, if our log slice needs to record the length of each interface call, we need to record the current time before and after the interface call, and then take the difference.

  • Before: Call the notification function before the target method is called;
  • Post Notification: The notification function is invoked After the target method is invoked, regardless of the method's return result;
  • AfterReturning: Invoke notification function after successful execution of target method;
  • AfterThrowing: Invokes the notification function after the target method throws an exception;
  • Around notification: The notification wraps the target method and executes custom behavior before and after the target method call.

JoinPoint

When notifications are applied, such as when an interface method is called, is the connection point to the log slice

Pointcut

The tangent points define the scope within which the notification function is applied.For example, log facets can be applied to all interfaces, which are the interface methods of all controller s.

Aspect

Aspects are a combination of notifications and tangent points that define when and where notifications should be applied

Introduction

Add a new method or property to an existing class without modifying it.

Weaving

The process of applying facets to the target object and creating a new proxy object.

Creating facets using annotations in Spring

Relevant Notes

  • @Aspect: Used to define facets
  • @Before: The notification method executes before the target method call
  • @After: The notification method executes after the target method returns or throws an exception
  • @AfterReturning: The notification method will execute after the target method returns
  • @AfterThrowing: The notification method executes after the target method throws an exception
  • @Around: The notification method encapsulates the target method
  • @Pointcut: Define the tangent expression

Tangent expression

Specifies the scope in which the notification will be applied, and the expression format:

Execution (package to which a method modifier returns a type method. class name. method name (method parameter)
//The public methods of all classes in the com.macro.mall.tiny.controller package apply the notifications in the facets
execution(public * com.macro.mall.tiny.controller.*.*(..))
//Notifications in facets apply to all methods in all classes under the com.macro.mall.tiny.service package and its subpackages
execution(* com.macro.mall.tiny.service..*.*(..))
//Notifications in facets apply to all methods in the com.macro.mall.tiny.service.PmsBrandService class
execution(* com.macro.mall.tiny.service.PmsBrandService.*(..))

Example

Add Log Information Encapsulation Class WebLog

Used to encapsulate log information that needs to be logged, including descriptions of operations, time consumed, url s, request parameters, and return results.

package com.macro.mall.tiny.dto;

/**
 * Controller Layer's Log Encapsulation Class
 * Created by macro on 2018/4/26.
 */
public class WebLog {
    /**
     * Pedagogical operation
     */
    private String description;

    /**
     * Operating Users
     */
    private String username;

    /**
     * Operation time
     */
    private Long startTime;

    /**
     * Time consumed
     */
    private Integer spendTime;

    /**
     * Root Path
     */
    private String basePath;

    /**
     * URI
     */
    private String uri;

    /**
     * URL
     */
    private String url;

    /**
     * Request Type
     */
    private String method;

    /**
     * IP address
     */
    private String ip;

    /**
     * Request parameters
     */
    private Object parameter;

    /**
     * Return of Request
     */
    private Object result;

    //getter,setter method omitted
}

Add Face Class WebLogAspect

A log facet is defined to get the information needed for the log in a surround notification and apply to all public methods in the controller layer.

/**
  * Unified Log Processing Face
  *
  * @author yisheng.mikelv@foxmail.com 2020/3/12 17:22
  */
@Aspect
@Component
@Order(1)
public class WebLogAspect {

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

    @Pointcut("execution(public * org.developer.es.api.controller.*.*(..))")
    public void webLog(){

    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable{

    }

    @AfterReturning(value = "webLog()", returning = "ret")
    public void doAfterReturning(Object ret) throws Throwable{

    }

    //Wrap Notification=Pre+Target Method Execution+Post Notification
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        long startTime = System.currentTimeMillis();
        //Get the current request object
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //Record request information
        WebLog webLog = new WebLog();
        //The proceed method is used to initiate the target method execution
        Object result = joinPoint.proceed();
        //Get Called Signature
        Signature signature = joinPoint.getSignature();
        //Convert to method signature
        MethodSignature methodSignature = (MethodSignature) signature;
        //Methods to get controller s
        Method method = methodSignature.getMethod();
        //If there is an ApiOperation annotation, get the value of the description
        if(method.isAnnotationPresent(ApiOperation.class)){
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            webLog.setDescription(apiOperation.value());
        }
        long endTime = System.currentTimeMillis();
        String urlStr = request.getRequestURL().toString();
        webLog.setBasePath(StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
        webLog.setIp(request.getRemoteUser());
        webLog.setMethod(request.getMethod());
        webLog.setParameter(getParameter(method, joinPoint.getArgs()));
        webLog.setResult(result);
        webLog.setSpendTime((int) (endTime - startTime));
        webLog.setStartTime(startTime);
        webLog.setUri(request.getRequestURI());
        webLog.setUrl(request.getRequestURL().toString());
        logger.info("{} \n", JSONUtil.parse(webLog));
        return result;


    }

    /**
     * Get request parameters based on method and parameters passed in
     */
    private Object getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        //Get the parameters of the method
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            //Use RequestBody comment-modified parameters as request parameters
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if(requestBody != null){
                argList.add(args[i]);
            }
            //RequestParam annotated parameters as request parameters
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (!StringUtils.isEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }

        }
        if (argList.size() == 0) {
            return null;
        } else if (argList.size() == 1) {
            return argList.get(0);
        } else {
            return argList;
        }
    }
}

Run result:

Expand

SpringBoot officially recommends logback-spring.xml

logback.xml: When an application is started, it is recognized directly by the log framework logback and used without going through SpringBoot

logback-spring.xml:The logback logging framework is not directly recognized, and the log configuration file is parsed by Spring Boot

SpringBoot loads logback-spring.xml

Reference resources:

logback profile loading process in Spring Boot

// org.springframework.boot.logging.logback.LogbackLoggingSystem
@Override
protected String[] getStandardConfigLocations() {
    return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy",
                         "logback.xml" };
}

Here you can see that the logback configuration file format supported in spring boot is to add'-spring'after the file name based on the logback-test.xml (logback-test.xml, logback.xml, etc.), such as logback-test-spring, logback-spring, etc.

/**
 * Return the spring config locations for this system. By default this method returns
 * a set of locations based on {@link #getStandardConfigLocations()}.
 * @return the spring config locations
 * @see #getSpringInitializationConfig()
 */
protected String[] getSpringConfigLocations() {
    String[] locations = getStandardConfigLocations();
    for (int i = 0; i < locations.length; i++) {
        String extension = StringUtils.getFilenameExtension(locations[i]);
        locations[i] = locations[i].substring(0,
                                              locations[i].length() - extension.length() - 1) + "-spring."
            + extension;
    }
    return locations;
}

Focus on the org.springframework.boot.logging.logback.LogbackLoggingSystem

Two original articles have been published. Approved 0. Visits 17
Private letter follow

Topics: Spring xml SpringBoot Programming