AOP如何实现记录操作日志

动态记录操作日志有利于项目的维护和故障的排查,可以通过AOP来实现。

依赖

<!--AOP起步依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>

数据库表设计

create table if not exists tb_operate_log
(
    id              bigint auto_increment comment '主键id'
        primary key,
    class_name      varchar(128)  not null comment '类名',
    method_name     varchar(128)  not null comment '方法名',
    method_param    varchar(1024) not null comment '方法参数',
    method_return   varchar(2048) not null comment '方法返回值',
    cost_time       bigint        not null comment '方法运行耗时;单位:ms',
    create_username varchar(16)   not null comment '方法调用者用户名',
    create_time     datetime      not null comment '创建时间',
    update_time     datetime      not null comment '更新时间',
    constraint id
        unique (id)
)
    comment '操作日志表';

OperateLogEntity实体类

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

import java.time.LocalDateTime;

/**
 * @Description: 操作日志表实体类
 * @Author: 翰戈.summer
 * @Date: 2023/11/21
 * @Param:
 * @Return:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OperateLogEntity {
    private Long id;
    private String className;
    private String methodName;
    private String methodParam;
    private String methodReturn;
    private Long costTime;
    private String createUsername;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

OperateLog枚举

/**
 * @Description: 日志操作类型
 * @Author: 翰戈.summer
 * @Date: 2023/11/21
 * @Param:
 * @Return:
 */
public enum OperateLog {

    //记录操作
    RECORD

}

RecordLog注解

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

/**
 * @Description: 注解,用于标识需要进行记录操作日志的方法
 * @Author: 翰戈.summer
 * @Date: 2023/11/21
 * @Param:
 * @Return:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RecordLog {

    //日志操作类型,RECORD记录操作
    OperateLog value();

}

OperateLogAspect切面类

import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
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.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * @Description: 切面类,实现记录操作日志
 * @Author: 翰戈.summer
 * @Date: 2023/11/21
 * @Param:
 * @Return:
 */
@Aspect
@Component
@RequiredArgsConstructor
public class OperateLogAspect {

    private final OperateLogMapper operateLogMapper;

    /**
     * @Description: 切入点
     * @Author: 翰戈.summer
     * @Date: 2023/11/21
     * @Param:
     * @Return: void
     */
    @Pointcut("execution(* com.demo.controller.*.*(..)) && @annotation(com.demo.annotation.RecordLog)")
    public void recordLogPointcut() {
    }

    /**
     * @Description: 环绕通知,进行记录操作日志
     * @Author: 翰戈.summer
     * @Date: 2023/11/21
     * @Param: ProceedingJoinPoint
     * @Return: Object
     */
    @Around("recordLogPointcut()")
    public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        //获取类名
        String className = proceedingJoinPoint.getTarget().getClass().getName();

        //获取方法名
        String methodName = proceedingJoinPoint.getSignature().getName();

        //获取方法参数
        Object[] args = proceedingJoinPoint.getArgs();
        String methodParam = Arrays.toString(args);

        Long begin = System.currentTimeMillis();// 方法运行开始时间
        Object result = proceedingJoinPoint.proceed();// 运行方法
        Long end = System.currentTimeMillis();// 方法运行结束时间

        //方法耗时
        Long costTime = end - begin;

        //获取方法返回值
        String methodReturn = JSONObject.toJSONString(result);

        String username = BaseContext.getContext();// 当前用户名
        LocalDateTime now = LocalDateTime.now();// 当前时间

        OperateLogEntity operateLog = new OperateLogEntity(null, className, methodName,
                methodParam, methodReturn, costTime, username, now, now);

        operateLogMapper.insertOperateLog(operateLog);

        return result;
    }
}

OperateLogMapper

import org.apache.ibatis.annotations.Mapper;

/**
 * @Description: 操作日志相关的数据库操作
 * @Author: 翰戈.summer
 * @Date: 2023/11/21
 * @Param:
 * @Return:
 */
@Mapper
public interface OperateLogMapper {
    void insertOperateLog(OperateLogEntity operateLog);
}
<?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.demo.mapper.OperateLogMapper">
    <!--记录操作日志-->
    <insert id="insertOperateLog">
        insert into tb_operate_log (id, class_name, method_name, method_param,
                                    method_return, cost_time, create_username, create_time, update_time)
        values (null, #{className}, #{methodName}, #{methodParam},
                #{methodReturn}, #{costTime}, #{createUsername}, #{createTime}, #{updateTime});
    </insert>
</mapper>

通过给controller层的接口方法加上@RecordLog(OperateLog.RECORD)注解即可实现记录操作日志,方便以后的问题排查。

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,需要定义一个注解来标识需要记录操作日志的方法: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Loggable { String value() default ""; } ``` 然后,定义一个切面类来实现记录操作日志的功能: ```java @Aspect @Component public class LoggingAspect { @Autowired private LogService logService; @Around("@annotation(loggable)") public Object logMethodExecution(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable { String methodName = joinPoint.getSignature().getName(); String className = joinPoint.getTarget().getClass().getSimpleName(); String message = loggable.value().isEmpty() ? methodName : loggable.value(); Object[] args = joinPoint.getArgs(); String argString = Arrays.toString(args); Object result = null; try { result = joinPoint.proceed(); logService.log(className, methodName, message, argString, result.toString(), LogType.INFO); } catch (Exception e) { logService.log(className, methodName, message, argString, e.getMessage(), LogType.ERROR); throw e; } return result; } } ``` 在切面类中,我们使用@Around注解来标识切面类型为Around,同时使用@annotation(loggable)来指定需要拦截的方法必须被@Loggable注解修饰。在切面方法中,我们可以获取到目标方法的类名、方法名、参数列表以及返回值,并把这些信息记录到数据库中。 最后,我们需要定义一个LogService类来实现操作日志记录到数据库的功能。具体实现可以根据项目的具体情况而定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值