Spring boot审计日志,一个注解实现全自动(请求接口,方法日志)记录

目录

1.使用注解

2.AOP实现

3.控制层

4.实体类

5.工具类

6.依赖

7.测试


1.使用注解

/**
 * 切面日志
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AuditLog {
    /**
     * 日志的名称:例如:操作劵
     * @return
     */
    String name();
    /**
     * Ognl表达式
     * 名称下面的标识:比如用户id,方便搜索
     * 使用方式:方法(String id,String name,UserDO userDo);
     * 获取id:idOgnl=id,获取name:idOgnl=name
     * 获取userDo的属性值 idOgnl=userDo.id
     * @return
     */
    String idOgnl() default "";
    /**
     * 类型:新增,修改
     * @return
     */
    String type() default "未知";
}

2.AOP实现

package com.dehua.aop;

import com.alibaba.fastjson.JSON;
import com.dehua.entity.AuditLogDO;
import com.dehua.util.CommonJoinPointUtil;
import lombok.extern.slf4j.Slf4j;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Map;

@Slf4j
@Aspect
@Order
@Component
public class AuditLogAop {

    //    @Resource
    //    private AuditLogService auditLogService;

    /**
     * 日志切入点
     */
    @Pointcut("@annotation(com.dehua.aop.AuditLog)")
    private void getLogPointCut() {
    }

    /**
     * 操作成功返回结果记录日志
     *
     * @param joinPoint
     * @param result
     */
    @AfterReturning(pointcut = "getLogPointCut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        AuditLog commonLog = method.getAnnotation(AuditLog.class);

        String auditName = commonLog.name();
        String idOgnl = commonLog.idOgnl();
        String auditType = commonLog.type();

        //        HttpServletRequest request = HttpServletUtil.getRequest();
        String requestURI = "test/user/add";//request.getRequestURI();
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Map<String, Object> paramMap = CommonJoinPointUtil.getMethodArgsAsMap(joinPoint);
        String resultJson = JSON.toJSONString(result);
        String traceId = "traceId";//TraceIdUtils.getTraceId();

        AuditLogDO auditLoDO = new AuditLogDO();
        auditLoDO.setAuditName(auditName);
        auditLoDO.setAuditNo(getAuditNo(idOgnl, paramMap));
        auditLoDO.setAuditType(auditType);
        auditLoDO.setReqUrl(requestURI);
        auditLoDO.setClassName(className);
        auditLoDO.setMethodName(methodName);
        auditLoDO.setParamJson(JSON.toJSONString(paramMap));
        auditLoDO.setResultJson(resultJson);
        auditLoDO.setTraceId(traceId);
        auditLoDO.setCreateDate(LocalDateTime.now());
        auditLoDO.setCreateUser("");

        log.info("---auditLoDO:{}---",JSON.toJSONString(auditLoDO));
        //        auditLogService.save(auditLoDO); 这里直接写库,可以优化成mq异步,日志量大可以按照时间分表
    }

    private String getAuditNo(String idOgnl, Map<String, Object> paramMap) {
        OgnlContext context = new OgnlContext();
        context.setRoot(paramMap);
        Object root = context.getRoot();
        try {
            return String.valueOf(Ognl.getValue(idOgnl, context, root));
        } catch (OgnlException e) {
            log.error("Ognl key:{} error:", idOgnl, e);
        }
        return idOgnl;
    }
}

3.控制层

可以在接口方法上使用,也可以在需要记录日志的方法上使用,

package com.dehua.controller;

import com.dehua.aop.AuditLog;
import com.dehua.entity.UserDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("test/user/")
public class UserController {

    @AuditLog(name = "操作用户", idOgnl = "userDO.name", type = "add")
    @PostMapping("add")
    public String add(UserDO userDO) {

        log.info("添加用户");

        return "ok";
    }

    @AuditLog(name = "操作用户", idOgnl = "userDO.id", type = "update")
    @PostMapping("update")
    public String update(UserDO userDO) {

        log.info("修改用户");

        return "ok";
    }
}

4.实体类

package com.dehua.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class AuditLogDO {
    private Long id;
    private String auditName;
    private String auditNo;
    private String auditType;
    private String reqUrl;
    private String className;
    private String methodName;
    private String paramJson;
    private String resultJson;
    private String traceId;
    private LocalDateTime createDate;
    private String createUser;
}
package com.dehua.entity;

import lombok.Data;

@Data
public class UserDO {

    private Long id;

    private String name;

    private Integer age;
}

5.工具类

package com.dehua.util;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

public class CommonJoinPointUtil {

    private CommonJoinPointUtil() {
    }
    /**
     * 获取切面的参数JSON
     *
     * @param joinPoint
     * @return
     */
    public static String getMethodArgsAsJson(JoinPoint joinPoint) {
        Map<String, Object> map = getMethodArgsAsMap(joinPoint);
        return JSON.toJSONString(map);
    }

    /**
     * 获取切面的参数Map
     * @param joinPoint
     * @return
     */
    public static Map<String, Object> getMethodArgsAsMap(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        // 参数名数组
        String[] parameterNames = ((MethodSignature) signature).getParameterNames();

        // 构造参数组集合
        Map<String, Object> map = new HashMap<>();
        Object[] args = joinPoint.getArgs();

        for (int i = 0; i < parameterNames.length; i++) {
            if (isUsefulParam(args[i])) {
                // 将参数转换为 JSON
                //                map.put(parameterNames[i], objectMapper.writeValueAsString(args[i]));
                map.put(parameterNames[i], args[i]);
            }
        }
        return map;
    }

    /**
     * 判断是否需要拼接的参数,过滤掉HttpServletRequest,MultipartFile,HttpServletResponse等类型参数
     *
     * @param arg
     * @return
     */
    private static boolean isUsefulParam(Object arg) {
        return !(arg instanceof MultipartFile) &&
        !(arg instanceof HttpServletRequest) &&
        !(arg instanceof HttpServletResponse);
    }
}

6.依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.11</version>
</dependency>

<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.0.8</version>
</dependency>

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

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>

7.测试

审计日志2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值