接口操作日志切面

1、AOP切面

/**
 * @Author wyq
 * @Description 操作的日志记录切面
 * @Date 2024/2/21 11:46
 **/
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
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.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

@Component
@Aspect
@Slf4j
public class MyOperateLogAspect {



    @Autowired
    HttpServletRequest httpServletRequest;

    /**
     * 操作日志记录
     */
    @Autowired
    private MyOperatorLogBiz operatorLogBiz;

    /**
     * 声明切面
     * 只要Controller的方法中有@log注解就切入 重要
     */
    @Pointcut("@annotation(annotation.MyOpreatorLog)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {

        try {
            // 获得注解
            MyOpreatorLog opreatorLog = getAnnotationLog(joinPoint);
            if (opreatorLog == null) {
                return;
            }
            // 获取当前的用户
            MyOperatorLogEntity logEntity = MyOperatorLogEntity.builder()
                    .requestType(httpServletRequest.getMethod())
                    .requestUrl(httpServletRequest.getRequestURI())
                    .build();
            //设置异常信息
            if(e == null){
                //当不需要记录返回结果时,设值为空串  add-2023/09/12
                logEntity.setRequestResult(opreatorLog.isSaveResultData()? JSON.toJSONString(jsonResult) : "-");
                logEntity.setLogLevel(LogLevelConstants.LOG_LEVEL_INFO);
            }else{
                //记录错误原因,根据日志记录的时间戳,通过ELK查看报错详情
                logEntity.setRequestResult(e.getMessage());
                logEntity.setLogLevel(LogLevelConstants.LOG_LEVEL_ERROR);
            }
            //设置当前登录人信息
            logEntity.setCrtTime(new Date());
            logEntity.setCrtName("登录人名称");
            logEntity.setCrtUser("登录人id");
            logEntity.setCrtJobNo("登录人工号");
            //处理设置注解上的参数
            getControllerMethodDescription(joinPoint, opreatorLog, logEntity);
            //保存您的日志数据到数据库(1.0使用openFeign,2.0使用restTemplate调用日志服务爆粗你接口)
            operatorLogBiz.addRecord(logEntity);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private MyOpreatorLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(MyOpreatorLog.class);
        }
        return null;
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param opreatorLog     日志
     * @param logEntity 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, MyOpreatorLog opreatorLog, MyOperatorLogEntity logEntity) throws Exception {
        // 设置action动作
        logEntity.setOperatorType(String.valueOf(opreatorLog.operatorType()));
        // 设置菜单名称
        logEntity.setMenuName(opreatorLog.menuName());
        // 是否需要保存request,参数和值
        if (opreatorLog.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, logEntity);
        }
    }

    /**
     * 获取请求的参数,放到log中
     *
     * @param logEntity 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, MyOperatorLogEntity logEntity) throws Exception {
        String requestMethod = logEntity.getRequestType();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            logEntity.setRequestParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
            logEntity.setRequestParam(StringUtils.substring(JSON.toJSONString(parameterMap), 0, 2000));
        }
    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (int i = 0; i < paramsArray.length; i++) {
                if (!isFilterObject(paramsArray[i])) {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest
                || o instanceof HttpServletResponse;
    }

}



2、切入点

/**
 * @Author wyq
 * @Description
 * @Date 2024/2/21 11:43
 **/
import java.lang.annotation.*;

/**
 * 日志管理 -- 操作日志注解
 */
@Documented
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyOpreatorLog {

    /**
     * 菜单路径
     */
    String menuName() default  "";


    /**
     * 操作类型
     */
    OperatorTypeConstant operatorType();

    /**
     * 是否保存请求数据
     */
    boolean isSaveRequestData() default true;

    /**
     * 是否保存返回结果,默认true add-2023/09/12
     */
    boolean isSaveResultData() default true;
}



3、日志级别及操作类型

/**
 * @Author wyq
 * @Description 操作日志等级分类
 * @Date 2024/2/21 12:00
 **/
public interface LogLevelConstants {

    String LOG_LEVEL_INFO = "INFO";

    String LOG_LEVEL_WARN = "WARN";
    String LOG_LEVEL_ERROR = "ERROR";
}

/**
 * @Author wyq
 * @Description 操作类型定制类
 * @Date 2024/2/21 11:44
 **/
public enum OperatorTypeConstant {

    SELECT("SELECT","查询"),
    ADD("ADD","新增"),
    EDIT("EDIT","修改"),
    DELETE("DELETE","删除"),
    IMPORT("IMPORT","导入"),
    EXPORT("EXPORT","导出"),
    COUNT("COUNT","统计");

    //操作标识
    private String mark;

    //操作名称
    private String name;

    OperatorTypeConstant(String mark,String name){
        this.mark = mark;
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public String getMark(){
        return mark;
    }
}

4、存储实体

<?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="MyOperatorLogMapper">

	<!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="MyOperatorLogEntity" id="myTransportCostOperatorLogMap">
        <result property="id" column="id"/>
        <result property="operatorType" column="operator_type"/>
        <result property="logLevel" column="log_level"/>
        <result property="menuName" column="menu_name"/>
        <result property="requestUrl" column="request_url"/>
        <result property="requestType" column="request_type"/>
        <result property="requestParam" column="request_param"/>
        <result property="requestResult" column="request_result"/>
        <result property="crtTime" column="crt_time"/>
        <result property="crtUser" column="crt_user"/>
        <result property="crtName" column="crt_name"/>
        <result property="crtJobNo" column="crt_job_no"/>
        <result property="crtHost" column="crt_host"/>
        <result property="status" column="status"/>
    </resultMap>
    <sql id="Base_Column_List">
        id, operator_type, log_level, menu_name, request_url, request_type, request_param, request_result, crt_time, crt_user, crt_name, crt_job_no, crt_host, status
    </sql>
</mapper>


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;


/**
 * 日志管理--操作日志
 *
 * @author wyq
 * @email 473664742@qq.com
 * @date 2022-09-16 18:42:12
 */
@Table(name = "my_operator_log")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MyOperatorLogEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @Id
    private String id;

    /**
     * 菜单名称
     */
    @Column(name = "menu_name")
    private String menuName;

    /**
     * 操作类型
     */
    @Column(name = "operator_type")
    private String operatorType;

    /**
     * 日志类型(INFO、WARN、ERROR)
     */
    @Column(name = "log_level")
    private String logLevel;

    /**
     * 请求url
     */
    @Column(name = "request_url")
    private String requestUrl;

    /**
     * 请求类型(GET、POST、PUT、DELETE)
     */
    @Column(name = "request_type")
    private String requestType;

    /**
     * 请求参数
     */
    @Column(name = "request_param")
    private String requestParam;

    /**
     * 请求结果(SUCCESS、FAIL)
     */
    @Column(name = "request_result")
    private String requestResult;

    /**
     * 创建时间
     */
    @Column(name = "crt_time")
    private Date crtTime;

    /**
     * 创建人
     */
    @Column(name = "crt_user")
    private String crtUser;

    /**
     * 创建人
     */
    @Column(name = "crt_name")
    private String crtName;

    /**
     * 创建工号
     */
    @Column(name = "crt_job_no")
    private String crtJobNo;

    /**
     * Mac/IP
     */
    @Column(name = "crt_host")
    private String crtHost;

    /**
     * 1:有效  -1:删除
     */
    @Column(name = "status")
    private Integer status;



}

5、引用方式
@MyOpreatorLog(menuName = “日志管理-操作日志-列表查询”,operatorType = OperatorTypeConstant.SELECT)

说明:

  • 支持附件类型的请求,但不存储附件内容,仅存储参数
  • menuName 可以接入到自己的权限系统通过url比较获取,没有权限的话,自己手写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值