天天看點

使用SpringBoot/Aspect實作日志埋點

在開發過程中,可能會遇到需要記錄使用者操作記錄的情況,這時候可以使用Spring的AOP實作日志埋點,記錄使用者的操作行為

實作步驟:

1.日志記錄注解(使用時直接添加到需要記錄的方法上)

package com.immo.jkzs.annotation;

import com.immo.jkzs.enums.OperationType;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperateLogAnnotation {
    String module() default "";    //子產品

    String remark() default "";    //備注

    OperationType operationType() default OperationType.UNKNOWN;//操作類型
}
           

2.操作類型

package com.immo.jkzs.enums;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;

/**
 * 操作類型(enum):主要是select,insert,update,delete
 */
@AllArgsConstructor
@Getter
public enum OperationType {

    /**
     * 操作類型
     */
    UNKNOWN("unknown"),
    DELETE("delete"),
    SELECT("select"),
    UPDATE("update"),
    INSERT("insert");

    private String value;
}
           

3.定義切面類

package com.immo.jkzs.aspect;

import com.immo.jkzs.annotation.OperateLogAnnotation;
import com.immo.jkzs.common.UserInfo;
import com.immo.jkzs.entity.BaseUser;
import com.immo.jkzs.entity.OperateLog;
import com.immo.jkzs.service.IOperateLogService;
import com.immo.jkzs.service.IRedisService;
import com.immo.jkzs.util.DateUtils;
import com.immo.jkzs.util.IdUtils;
import com.immo.jkzs.util.IpAddressUtil;
import com.immo.jkzs.util.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
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.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 使用者記錄檔
 *
 * @author Jonathan.WQ
 */
@Aspect
@Component
public class OperationLogAspect {
    
    @Autowired
    private IOperateLogService operateLogService;

    @Pointcut("@annotation(com.immo.jkzs.annotation.OperateLogAnnotation)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object result = null;
        //子產品開始時間
        long start = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        OperateLog operateLog = new OperateLog();
        operateLog.setId(IdUtils.getInstanse().getUID());
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        // 設定請求的方法名
        operateLog.setMethod(className + "." + methodName + "()");
        // 請求的方法參數值
        Object[] args = joinPoint.getArgs();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        if (args != null && paramNames != null) {
            StringBuilder paramsBuilder = new StringBuilder();
            for (int i = 0; i < args.length; i++) {
                String paramName = paramNames[i];
                Object arg = args[i];
                //考慮到參數過長的情況,故而截取
                paramsBuilder.append(paramName).append(": ");
                String paramValue = String.valueOf(arg);
                if (paramValue.length() > 50) {
                    paramsBuilder.append(paramValue.substring(0, 50));
                } else {
                    paramsBuilder.append(paramValue);
                }
                paramsBuilder.append("...");
                if (i != args.length - 1) {
                    paramsBuilder.append("  ,");
                }
            }
            // 設定請求的方法參數名稱
            operateLog.setParameter(paramsBuilder.toString());
        }
        // 擷取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 設定IP位址
        operateLog.setIp(IpAddressUtil.getIpAddr(request));
        // 擷取使用者
        String authorization = request.getParameter("Authorization");
        if (StringUtils.isNotNull(authorization)) {
            BaseUser user = UserInfoThreadLocal.getUser();
            operateLog.setUserName(user.getName());
            operateLog.setUserId(user.getId());
        }
        //擷取系統時間
        operateLog.setCreateTime(DateUtils.getCurrentDate());
        OperateLogAnnotation OperateLogAnnotation = method.getAnnotation(OperateLogAnnotation.class);
        if (OperateLogAnnotation != null) {
            // 注解上的描述
            try {
                operateLog.setModule(OperateLogAnnotation.module());
                operateLog.setRemark(OperateLogAnnotation.remark());
                operateLog.setOperationType(OperateLogAnnotation.operationType().name());
                result = joinPoint.proceed();
                long end = System.currentTimeMillis();
                //将計算好的時間儲存在實體中
                operateLog.setResponseTime("" + (end - start));
                operateLog.setCommit("success");
                //儲存進資料庫
                operateLogService.save(operateLog);
            } catch (Throwable e) {
                operateLog.setModule(OperateLogAnnotation.module());
                long end = System.currentTimeMillis();
                operateLog.setResponseTime("" + (end - start));
                operateLog.setCommit("failed");
                operateLog.setExceptionType(e.getClass().getName());
                //儲存進資料庫
                operateLogService.save(operateLog);
            }
        }
        return result;
    }
}
           

4.使用者記錄檔

package com.immo.jkzs.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;

/**
 * 使用者操作記錄表
 *
 * @author Jonathan.WQ
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("operate_log")
public class OperateLog implements Serializable {

    private static final long serialVersionUID = 1L;


    /**
     * 主鍵
     */
    @TableId(value = "id", type = IdType.INPUT)
    private String id;//

    /**
     * 使用者ID
     */
    @TableField("user_id")
    private String userId;


    /**
     * 使用者昵稱
     */
    @TableField("user_name")
    private String userName;


    /**
     * 使用者類型
     */
    @TableField("user_type")
    private String userType;


    /**
     * 通路子產品
     */
    @TableField("module")
    private String module;

    /**
     * 操作類型
     */
    @TableField("operation_type")
    private String operationType;


    /**
     * 備注
     */
    @TableField("remark")
    private String remark;


    /**
     * 通路方法
     */
    @TableField("method")
    private String method;


    /**
     * 通路參數
     */
    @TableField("parameter")
    private String parameter;


    /**
     * 響應時間ms
     */
    @TableField("response_time")
    private String responseTime;


    /**
     * 建立時間
     */
    @TableField("create_time")
    private Date createTime;


    /**
     * 執行結果
     */
    @TableField("commit")
    private String commit;


    /**
     * 異常類型
     */
    @TableField("exception_type")
    private String exceptionType;


    /**
     * IP位址
     */
    @TableField("ip")
    private String ip;
}
           

5.使用注解

@OperateLogAnnotation(remark = "新增意見回報",operationType= OperationType.INSERT)
@PostMapping("/add")
public CommonResult addHandler(@RequestParam("feedBack") FeedBackDTO feedbackDTO, HttpServletRequest request) {
        return feedbackService.add(feedbackDTO, request);
}