AOP behavior log

Recently, new projects need to record behavior logs. I haven't used AOP for a long time. I have studied it.

How much nonsense to fill? First, the previous flow chart:

Design of database log table

Field name Field type notes
LOG_ID VARCHAR2(255)  
LOG_LEVEL  NUMBER Log level
START_TIME  DATE Start time
RUN_TIME  NUMBER Running time (ms)
OPERATION_MODULE  VARCHAR2(255) Module operated
OPERATION_UNIT  VARCHAR2(255) Operated unit
OPERATION_TYPE  VARCHAR2(255) Operation type
OPERATION_DETAIL  VARCHAR2(500 CHAR) Operation details
USER_CODE  VARCHAR2(255) User number
USER_NAME  VARCHAR2(255) User name

 

 

 

 

 

 

 

 

 

 

 

 

Note: Oracle used by database

JAVA end

1. Create log entity class

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

@Data
public class OperationLog {
    private String logId;
    private String userCode;
    private String userName;
    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
    private Date startTime;
    private Long runTime;
    private String operationUnit;
    private String operationType;
    private String operationDetail;
    private String operationModule;
    private Integer logLevel;
}

2. Create enumeration classes such as log operation type, unit, module, etc

(1) Operation module enumeration class

public enum OperationModule {
    /**
     * Module operated
     */
    UNKNOWN("XX system"),
    USER("User module"),
    PRODUCT("Product module"),
    SALE("Sales information module");

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    OperationModule(String s) {
        this.value = s;
    }
}

(2) Operation unit enumeration class

public enum OperationUnit {
    /**
     * Operated unit
     */
    UNKNOWN(""),
    /**
     * User module
     */
    USER_INFO("User information"),
    USER_ROLE("User role"),
    USER_PERMISSION("User rights"),
    /**
     * Product module
     */
    PRODUCT_INFO("Product information"),
    PRODUCT_INV("Product inventory"),
    /**
     * Sales information module
     */
    SALE_INFO("Sales information"),
    SALE_PLAN("Sales plan");


    private String value;

    OperationUnit(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

(3) Operation type enumeration class

public enum OperationType {
    /**
     * Basic operation type
     */
    UNKNOWN(""),
    LOGIN("Sign in"),
    INPUT("Import"),
    QUERY("query"),
    EXPORT("export"),
    DELETE("delete"),
    INSERT("insert"),
    UPDATE("to update"),
    /**
     * user
     */
    USER_SET_ROLE("Set user role"),
    USER_SET_PERMISSION("Set user permissions"),
    /**
     * commodity
     */
    PRODUCT_EXPORT_INFO("Export product information"),
    PRODUCT_SET_RANK("Set product level"),
    /**
     * sale
     */
    SALE_EXPORT_INFO("Export sales information"),
    SALE_SET_SALE_PLAN("Set up sales plan");

    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    OperationType(String s) {
        this.value = s;
    }
}

3. Create log annotation

import com.XXX.XXX.domin.OperationModule;
import com.XXX.XXX.domin.OperationType;
import com.XXX.XXX.domin.OperationUnit;

import java.lang.annotation.*;

@Documented //Indicates that the comment should be javadoc Tool record
@Target({ElementType.METHOD})   //Declare that the annotation acts on the method
@Retention(RetentionPolicy.RUNTIME) //Declare that the annotation is not only saved to class In the file, jvm load class After the file, it still exists
public @interface OperationLogDetail {

    /**
     * Method description, you can use placeholders to get parameters: {{param}}}
     */
    String operationDetail() default "";

    /**
     * Log level: 1-9
     */
    int logLevel() default 1;

    /**
     * Operation type (enum)
     */
    OperationType operationType() default OperationType.UNKNOWN;

    /**
     * Objects to be manipulated (enum is used here)
     */
    OperationUnit operationUnit() default OperationUnit.UNKNOWN;

    /**
     * Operating system module (enum is used here)
     */
    OperationModule operationModule() default OperationModule.UNKNOWN;

}

4. Create AOP method, using surround notification

@Aspect     //Indicates that this class is a tangent
@Component  //Instantiate to spring In container
public class OperationLogAop {

    @Autowired
    private OperationLogService operationLogService;
    
    //It indicates that the tangent point is added OperationLogDetail Method of annotation
    @Pointcut("@annotation(com.topsports.adbuhuo.annotation.OperationLogDetail)")
    public void operationLog(){}
    
    //Around Advice 
    @Around("operationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        //Get system current time
        long time = System.currentTimeMillis();
        try {
            //Get the parameters of the pointcut (the method to log)
            Object[] args = joinPoint.getArgs();
            //Call the method to log
            res =  joinPoint.proceed(args);
            //Get method execution time
            time = System.currentTimeMillis() - time;
            return res;
        } finally {
            try {
                //Add log after method execution
                addOperationLog(joinPoint,res,time);
            }catch (Exception e){
                System.out.println("LogAspect Operation failed:" + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    private void addOperationLog(JoinPoint joinPoint, Object res, long time){
        //Get the currently logged in user
        UserInfo userInfo = SecurityUserUtil.getThisUserInfo();
        //Obtain the signature of the method, which is used to obtain the annotation added to the method
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        //Create log object
        OperationLog operationLog = new OperationLog();
        operationLog.setRunTime(time);
        operationLog.setLogId(UUID.randomUUID().toString());
        operationLog.setStartTime(new Date());
        operationLog.setUserName(userInfo.getUserName());
        operationLog.setUserCode(userInfo.getUserCode());
        //Get comments on Methods
        OperationLogDetail annotation = signature.getMethod().getAnnotation(OperationLogDetail.class);
        if(annotation != null){
            operationLog.setLogLevel(annotation.logLevel());
            operationLog.setOperationDetail(getDetail(((MethodSignature)joinPoint.getSignature()).getParameterNames(),joinPoint.getArgs(),annotation));
            operationLog.setOperationType(annotation.operationType().getValue());
            operationLog.setOperationUnit(annotation.operationUnit().getValue());
            operationLog.setOperationModule(annotation.operationModule().getValue());
        }
        //Save log
        operationLogService.insertSystemLog(operationLog);
    }


    /**
     * Handle placeholders
     * @param argNames Method parameter name array
     * @param args Method parameter array
     * @param annotation Annotation information
     * @return Return the processed description
     */
    private String getDetail(String[] argNames, Object[] args, OperationLogDetail annotation){

        Map<Object, Object> map = new HashMap<>(4);
        for(int i = 0;i < argNames.length;i++){
            map.put(argNames[i],args[i]);
        }
        //Get details
        String detail = annotation.operationDetail();
        try {
            //Traverse the parameters of the incoming method
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                //request and response Not serializable, XSSFWorkbook Also not serializable
                if(!(v instanceof HttpServletRequest) && !(v instanceof HttpServletResponse) && !(v instanceof XSSFWorkbook)){
                    if(v instanceof JSONObject){
                        //handle JSONObject Format parameters
                        JSONObject jsonObject = (JSONObject) v;
                        for (String jk : jsonObject.keySet()) {
                            detail = detail.replace("{{" + jk + "}}", jsonObject.get(jk)!=null?jsonObject.get(jk).toString():"");
                        }
                    }else{
                        detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
                    }
                }else if(v instanceof HttpServletRequest){
                    //handle HttpServletRequest
                    JSONObject jsonObject = CommonUtil.request2Json((HttpServletRequest) v);
                    for (String jk : jsonObject.keySet()) {
                        detail = detail.replace("{{" + jk + "}}", jsonObject.get(jk)!=null?jsonObject.get(jk).toString():"");
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return detail;
    }

}

5. Create Service and Mapper

public interface OperationLogService {
    //insert
    void insertSystemLog(OperationLog operationLog);
}
--------------------------------------------------
@Service
public class OperationLogServiceImpl implements OperationLogService {

    @Autowired
    private OperationLogMapper operationLogMapper;
    
    @Override
    public void insertSystemLog(OperationLog operationLog) {
        operationLogMapper.insertSystemLog(operationLog);
    }
}
--------------------------------------------------
@Mapper
@Repository
public interface OperationLogMapper {
    void insertSystemLog(OperationLog operationLog);
}
---------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.XXX.XXX.dao.OperationLogMapper">
    <insert id="insertSystemLog" parameterType="com.XXX.XXX.domin.OperationLog">
        insert into SYSTEM_OPERATION_LOG(
        LOG_ID,
        USER_CODE,
        USER_NAME,
        START_TIME,
        RUN_TIME,
        OPERATION_UNIT,
        OPERATION_TYPE,
        OPERATION_DETAIL,
        LOG_LEVEL,
        OPERATION_MODULE
        )
        values(
        #{logId},
        #{userCode},
        #{userName},
        #{startTime},
        #{runTime},
        #{operationUnit},
        #{operationType},
        #{operationDetail},
        #{logLevel},
        #{operationModule}
        )
    </insert>
</mapper>

use

Add the created annotation to the method you want to log

@OperationLogDetail(
    operationDetail = "{{userCode}}",   //This placeholder will scan the parameter list for
    operationType = OperationType.QUERY,
    operationUnit = OperationUnit.USER_INFO,
    operationModule = OperationModule.USER)
@PostMapping("/getUserInfo")
public JSONObject getUserInfo(@RequestBody JSONObject jsonObject){
    return userInfoService.getUserInfo(jsonObject);
}

Tags: Java Mybatis Database Oracle

Posted on Sun, 17 May 2020 10:17:05 -0400 by stone.cold.steve.austin