利用AOP实现日志记录
本文章仅用于学习记录,学习的原文章链接在下方,需要的同学可以去看看。链接: https://blog.csdn.net/m0_37459380/article/details/82696867
需求分析
针对数据库的操作记录进行日志记录,可以让我们在排查问题时更加方便快捷,在实现这个需求之前,我们首先需要思考一下这个需求的实现流程
- 需要一个实体类来记录相关信息
- 需要一个自定义注解进行全局适配
- 需要一个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等框架的日志的功能和全局异常处理也是类似的原理。