SpringBoot AOP records operation logs and exception logs

Posted by Arez on Thu, 02 Dec 2021 05:43:36 +0100

We often need to record the operation of some important functions when we are doing projects, so as to track who is operating this function in the future; Exceptions may occur when we operate some functions, but we need to locate the reason for each exception. We have to go to the server to query the log, and we can't count the exceptions, so as to improve our project. It would be good if we could make a special function to record the operation log and exception log, Of course, we certainly have methods to do this, and it won't be difficult. We can add the code to record the log in the required methods and the code to record exceptions in each method, and finally save the recorded log to the database. It sounds easy, but when we do it, we will find that this work is very cumbersome, and we are doing some repetitive work and adding a lot of redundant code. It is certainly not feasible to record logs in this way.

        We have learned the three major features of Spring, IOC (control inversion), DI (dependency injection) and AOP (aspect oriented). The main function of AOP is to separate the logging, performance statistics, security control, transaction processing, exception handling and other codes from the business logic code. Today, we'll use springBoot Aop for logging. Well, after a lot of nonsense, we'd better ship the goods.

1, Create a log table with the following structure:

Operation log table

  

Exception log table

      

2, Add Maven dependency

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

3, Create operation log annotation class

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Custom action log annotation
 */
@Target(ElementType.METHOD) //Target location for annotation placement,METHOD It can be annotated at the method level
@Retention(RetentionPolicy.RUNTIME) //At what stage do annotations execute
@Documented 
public @interface OperLog {
    String operModul() default ""; // Operation module
    String operType() default "";  // Operation type
    String operDesc() default "";  // Operating instructions
}

4, Create a section class to record the operation log

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.gexin.fastjson.JSON;
import com.hyd.zcar.cms.common.utils.IPUtil;
import com.hyd.zcar.cms.common.utils.annotation.OperLog;
import com.hyd.zcar.cms.common.utils.base.UuidUtil;
import com.hyd.zcar.cms.common.utils.security.UserShiroUtil;
import com.hyd.zcar.cms.entity.system.log.ExceptionLog;
import com.hyd.zcar.cms.entity.system.log.OperationLog;
import com.hyd.zcar.cms.service.system.log.ExceptionLogService;
import com.hyd.zcar.cms.service.system.log.OperationLogService;

/**
 * Section processing class, operation log, exception log record processing
 */
@Aspect
@Component
public class OperLogAspect {

    /**
     * Operation version number
     * <p>
     * When the project is started, it is passed from the command line, for example: java -jar xxx.war --version=201902
     * </p>
     */
    @Value("${version}")
    private String operVer;

    @Autowired
    private OperationLogService operationLogService;

    @Autowired
    private ExceptionLogService exceptionLogService;

    /**
     * Set the operation log entry point to record the entry code of the operation log at the annotation position
     */
    @Pointcut("@annotation(com.hyd.zcar.cms.common.utils.annotation.OperLog)")
    public void operLogPoinCut() {
    }

    /**
     * Set the operation exception entry point, record the exception log, and scan all operations under the controller package
     */
    @Pointcut("execution(* com.hyd.zcar.cms.controller..*.*(..))")
    public void operExceptionLogPoinCut() {
    }

    /**
     * It returns the notification normally, intercepts the user operation log, and executes after the normal execution of the connection point. If the connection point throws an exception, it will not execute
     * 
     * @param joinPoint breakthrough point
     * @param keys      Return results
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // obtain RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // Get from RequestAttributes Get in HttpServletRequest Information about
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OperationLog operlog = new OperationLog();
        try {
            operlog.setOperId(UuidUtil.get32UUID()); // Primary key ID

            // Method for obtaining weaving in point through reflection mechanism from cutting in point
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // Get the method where the pointcut is located
            Method method = signature.getMethod();
            // Get operation
            OperLog opLog = method.getAnnotation(OperLog.class);
            if (opLog != null) {
                String operModul = opLog.operModul();
                String operType = opLog.operType();
                String operDesc = opLog.operDesc();
                operlog.setOperModul(operModul); // Operation module
                operlog.setOperType(operType); // Operation type
                operlog.setOperDesc(operDesc); // pedagogical operation
            }
            // Gets the requested class name
            String className = joinPoint.getTarget().getClass().getName();
            // Gets the requested method name
            String methodName = method.getName();
            methodName = className + "." + methodName;

            operlog.setOperMethod(methodName); // Request method

            // Requested parameters
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // Convert the array of parameters to json
            String params = JSON.toJSONString(rtnMap);

            operlog.setOperRequParam(params); // Request parameters
            operlog.setOperRespParam(JSON.toJSONString(keys)); // Return results
            operlog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // Request user ID
            operlog.setOperUserName(UserShiroUtil.getCurrentUserName()); // Request user name
            operlog.setOperIp(IPUtil.getRemortIP(request)); // request IP
            operlog.setOperUri(request.getRequestURI()); // request URI
            operlog.setOperCreateTime(new Date()); // Creation time
            operlog.setOperVer(operVer); // Operation version
            operationLogService.insert(operlog);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * The exception return notification is used to intercept the exception log information and execute after the connection point throws an exception
     * 
     * @param joinPoint breakthrough point
     * @param e         Abnormal information
     */
    @AfterThrowing(pointcut = "operExceptionLogPoinCut()", throwing = "e")
    public void saveExceptionLog(JoinPoint joinPoint, Throwable e) {
        // obtain RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // Get from RequestAttributes Get in HttpServletRequest Information about
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        ExceptionLog excepLog = new ExceptionLog();
        try {
            // Method for obtaining weaving in point through reflection mechanism from cutting in point
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // Get the method where the pointcut is located
            Method method = signature.getMethod();
            excepLog.setExcId(UuidUtil.get32UUID());
            // Gets the requested class name
            String className = joinPoint.getTarget().getClass().getName();
            // Gets the requested method name
            String methodName = method.getName();
            methodName = className + "." + methodName;
            // Requested parameters
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            // Convert the array of parameters to json
            String params = JSON.toJSONString(rtnMap);
            excepLog.setExcRequParam(params); // Request parameters
            excepLog.setOperMethod(methodName); // Request method name
            excepLog.setExcName(e.getClass().getName()); // Exception name
            excepLog.setExcMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())); // Abnormal information
            excepLog.setOperUserId(UserShiroUtil.getCurrentUserLoginName()); // operator ID
            excepLog.setOperUserName(UserShiroUtil.getCurrentUserName()); // Operator name
            excepLog.setOperUri(request.getRequestURI()); // operation URI
            excepLog.setOperIp(IPUtil.getRemortIP(request)); // operator IP
            excepLog.setOperVer(operVer); // Operation version number
            excepLog.setOperCreateTime(new Date()); // Abnormal time

            exceptionLogService.insert(excepLog);

        } catch (Exception e2) {
            e2.printStackTrace();
        }

    }

    /**
     * Transform request parameters
     * 
     * @param paramMap request Gets the parameter array of the
     */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }

    /**
     * Convert exception information to string
     * 
     * @param exceptionName    Exception name
     * @param exceptionMessage Abnormal information
     * @param elements         Stack information
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
}

5, Add @ OperLog annotation to the Controller layer method

6, Operation log and exception log query function