本文演示下如何使用AOP,去实现系统操作日志功能。
实现步骤
- 引入AOP包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.6</version>
</dependency>
- 定义数据模型
package com.angel.ocean.domain.entity;
import lombok.Data;
import java.util.Date;
@Data
public class Syslog {
/**
* 主键
**/
private Long id;
/**
* ip 地址
**/
private String ip;
/**
* 类名
**/
private String className;
/**
* 方法名称
**/
private String methodName;
/**
* 传参
**/
private String params;
/**
* 执行结果, true-成功,false-失败
**/
private boolean status;
/**
* 响应信息
**/
private String response;
/**
* 业务类型
**/
private String remark;
/**
* 触发时间
**/
private Date createTime;
/**
* 操作用户
**/
private String createBy;
}
- 定义系统日志注解SyslogAnno
package com.angel.ocean.annotation;
import java.lang.annotation.*;
/**
* 定义系统日志注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SyslogAnno {
String value() default "";
}
- 定义切面SyslogAspect
package com.angel.ocean.aspect;
import com.alibaba.fastjson2.JSON;
import com.angel.ocean.annotation.SyslogAnno;
import com.angel.ocean.domain.entity.Syslog;
import com.angel.ocean.runner.SyslogHandlerTask;
import com.angel.ocean.util.LogDataUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
1. 日志切面
*/
@Slf4j
@Aspect
@Component
public class SyslogAspect {
@Pointcut("@annotation(com.angel.ocean.annotation.SyslogAnno)")
public void logPointCut() {
}
@AfterReturning(value = "logPointCut()", returning = "result")
public void normalLog(JoinPoint point, Object result) {
try {
Syslog syslog = getSysLog(point);
syslog.setStatus(true);
syslog.setResponse(JSON.toJSONString(result));
SyslogHandlerTask.LOG_QUEUE.offer(syslog);
} catch (Exception e) {
log.error("SyslogAspect.normalLog() error. ", e);
}
}
@AfterThrowing(value = "logPointCut()", throwing = "throwable")
public void exceptionLog(JoinPoint point, Throwable throwable) {
try {
Syslog syslog = getSysLog(point);
syslog.setStatus(false);
syslog.setResponse(throwable.getMessage());
SyslogHandlerTask.LOG_QUEUE.offer(syslog);
} catch (Exception e) {
log.error("SyslogAspect.exceptionLog() error. ", e);
}
}
private Syslog getSysLog(JoinPoint joinPoint) {
Syslog sysLog = new Syslog();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
SyslogAnno sysLogAnno = method.getAnnotation(SyslogAnno.class);
if (sysLogAnno != null) {
sysLog.setRemark(sysLogAnno.value());
}
// 请求的 类名、方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setClassName(className);
sysLog.setMethodName(methodName);
// 请求的参数
Object[] args = joinPoint.getArgs();
try {
List<String> list = new ArrayList<>();
for (Object o : args) {
list.add(JSON.toJSONString(o));
}
sysLog.setParams(list.toString());
} catch (Exception e) {
log.error("SyslogAspect.saveLog() error.", e);
}
sysLog.setIp(LogDataUtil.getIP());
sysLog.setCreateBy(LogDataUtil.getUserId());
sysLog.setCreateTime(new Date());
return sysLog;
}
}
工具类
package com.angel.ocean.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Slf4j
public class LogDataUtil {
private LogDataUtil() {}
/**
* 获取用户IP地址
*/
public static String getIP() {
String ip = "";
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ip = request.getRemoteAddr();
} catch (Exception e) {
log.error("GetIPException", e);
}
return ip;
}
/**
* 获取用户ID
*/
public static String getUserId() {
String userId = "";
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
userId = request.getHeader("userId");
} catch (Exception e) {
log.error("GetUserIdException", e);
}
return userId;
}
}
- 定义日志数据处理SyslogHandlerTask
package com.angel.ocean.runner;
import cn.hutool.core.collection.CollUtil;
import com.angel.ocean.domain.entity.Syslog;
import com.angel.ocean.service.SyslogService;
import com.google.common.collect.Queues;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class SyslogHandlerTask implements CommandLineRunner {
@Resource
private SyslogService syslogService;
/**
* 操作日志队列
*/
public static final BlockingQueue<Syslog> LOG_QUEUE = new LinkedBlockingQueue<>();
@Override
public void run(String... strings) throws Exception {
new Thread(() -> {
List<Syslog> SyslogList = new ArrayList<>();
while (true) {
try {
Queues.drain(LOG_QUEUE, SyslogList, 100, 500, TimeUnit.MILLISECONDS);
if (CollUtil.isNotEmpty(SyslogList)) {
syslogService.batchInsert(SyslogList);
SyslogList.clear();
} else {
Thread.sleep(500);
}
} catch (Exception e) {
log.error("SyslogHandlerTask error={}", e.getMessage(), e);
}
}
}, "Syslog_Props_Mysql").start();
}
}
- 打包成系统操作日志SDK (ocean-log)
如何使用
继承系统操作日志SDK
<dependency>
<groupId>com.angel.ocean</groupId>
<artifactId>ocean-log</artifactId>
<version>1.0.0</version>
</dependency>
在Api的添加/修改/删除等方法上,添加SyslogAnno注解
@ApiModelProperty(value = "保存角色信息表")
@PostMapping("save")
@SyslogAnno("添加角色")
public ApiResult<?> save(@RequestBody SysRoleDTO dto) {
service.save(dto);
return ApiResult.success();
}
实例验证
接口调用
系统操作日志数据