Aop实现日志记录

利用AOP实现日志记录

本文章仅用于学习记录,学习的原文章链接在下方,需要的同学可以去看看。链接: https://blog.csdn.net/m0_37459380/article/details/82696867

需求分析

针对数据库的操作记录进行日志记录,可以让我们在排查问题时更加方便快捷,在实现这个需求之前,我们首先需要思考一下这个需求的实现流程

  1. 需要一个实体类来记录相关信息
  2. 需要一个自定义注解进行全局适配
  3. 需要一个aop的配置类对日志的具体操作进行记录

依赖

<parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.5.4</version>
       <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.guanghui</groupId>
   <artifactId>aoplog</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>aoplog</name>
   <description>Demo project for Spring Boot</description>
   <properties>
       <java.version>1.8</java.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-devtools</artifactId>
           <scope>runtime</scope>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-aop</artifactId>
       </dependency>

       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.41</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>
   </dependencies>

配置类

server:
  port: 8081
spring:
  aop:
    auto: true #开启aop

包结构

在这里插入图片描述

实体类

import lombok.Data;

import java.util.Date;

@Data
public class OperateLog {
    private String id;
    private Date createtime;
    /**
     * 日志等级
     */
    private Integer level;

    /**
     * 方法名
     */
    private String method;
    /**
     * 参数
     */
    private String args;
    /**
     * 操作人id
     */
    private String userId;
    /**
     * 操作人
     */
    private String userName;
    /**
     * 日志描述
     */
    private String describe;
    /**
     * 操作类型
     */
    private String operationType;
    /**
     * 方法运行时间
     */
    private Long runTime;
    /**
     * 方法返回值
     */
    private String returnValue;

}

枚举类

public enum OpreateType {
    UNKNOWN("unknown"),
    DELETE("delete"),
    SELECT("select"),
    UPDATE("update"),
    INSERT("insert");

    private String value;

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

自定义注解类

import com.guanghui.aoplog.enums.OpreateType;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogDetail {
    String detail()default "";

    //日志级别
    int  level() default 0;
    //操作类型
    OpreateType operateType() default OpreateType.UNKNOWN;
}

aop配置类

import com.alibaba.fastjson.JSON;
import com.guanghui.aoplog.annotation.LogDetail;
import com.guanghui.aoplog.model.OperateLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.annotation.Target;
import java.util.Date;
import java.util.UUID;

@Aspect
@Component
public class LogAspect {

    //为自定义注解增加切点,此处也可通过方法增加切点
    @Pointcut("@annotation(com.guanghui.aoplog.annotation.LogDetail)")
    public void operateLog(){}

    @Around("operateLog()")
    public Object  doAround(ProceedingJoinPoint joinPoint){
        long time=System.currentTimeMillis();
        Object o=null;
        try {
             o=joinPoint.proceed();

        }catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            //方法执行完成后执行日志
            addOperationLog(joinPoint,o,time);
        }
        return o;
    }

    private void addOperationLog(JoinPoint joinPoint, Object res, long time){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        OperateLog operationLog = new OperateLog();
        operationLog.setRunTime(time);
        operationLog.setReturnValue(JSON.toJSONString(res));
        operationLog.setId(UUID.randomUUID().toString());
        operationLog.setArgs(JSON.toJSONString(joinPoint.getArgs()));
        operationLog.setCreatetime(new Date());
        operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        operationLog.setUserId("#{currentUserId}");
        operationLog.setUserName("#{currentUserName}");
        LogDetail annotation = signature.getMethod().getAnnotation(LogDetail.class);
        if(annotation != null){
            operationLog.setLevel(annotation.level());
            //operationLog.setDescribe(getDetail(((MethodSignature)joinPoint.getSignature()).getParameterNames(),joinPoint.getArgs(),annotation));
            operationLog.setOperationType(annotation.operateType().getValue());
            //operationLog.setOperationUnit(annotation.operationUnit().getValue());
        }
        //TODO 这里保存日志
        System.out.println("记录日志:" + operationLog.toString());
//        operationLogService.insert(operationLog);
    }

    @Before("operateLog()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        System.out.println("进入方法前执行.....");

    }

    /**
     * 处理完请求,返回内容
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "operateLog()")
    public void doAfterReturning(Object ret) {
        System.out.println("方法的返回值 : " + ret);
    }

    /**
     * 后置异常通知
     */
    @AfterThrowing("operateLog()")
    public void throwss(JoinPoint jp){
        System.out.println("方法异常时执行.....");
    }


    /**
     * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
     */
    @After("operateLog()")
    public void after(JoinPoint jp){
        System.out.println("方法最后执行.....");
    }
}

Service

import org.springframework.stereotype.Service;

public interface UserService {
    String findUserName(String id);
}

Service实现类

import com.guanghui.aoplog.annotation.LogDetail;
import com.guanghui.aoplog.enums.OpreateType;
import com.guanghui.aoplog.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Override
    @LogDetail(detail = "通过id[{{tel}}]获取用户名",level = 3,operateType = OpreateType.SELECT)
    public String findUserName(String id) {
        System.out.println("id = " + id);
        return "guanghui";
    }
}

测试类

import com.guanghui.aoplog.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

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

    @Autowired
    private UserService userService;

    @RequestMapping("/get")
    public String get(@RequestParam("id") String id){
        return userService.findUserName(id);
    }
}

测试结果

进入方法前执行.....
id = 1
方法的返回值 : guanghui
方法最后执行.....
记录日志:OperateLog(id=647d0e67-6160-497f-aeaa-dcfe348439ff, createtime=Sun Sep 19 22:52:12 CST 2021, level=3, 
method=com.guanghui.aoplog.service.serviceimpl.UserServiceImpl.findUserName, 
args=["1"], 
userId=#{currentUserId}, 
userName=#{currentUserName}, describe=null, 
operationType=select, runTime=1632063132867, returnValue="guanghui")

本此测试实现的是操作数据日志的记录,如果想实现类似于slfj等框架的日志的功能和全局异常处理也是类似的原理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值