AOP应用之系统操作日志
本文演示下如何使用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 {@Resourceprivate SyslogService syslogService;/*** 操作日志队列*/public static final BlockingQueue<Syslog> LOG_QUEUE = new LinkedBlockingQueue<>();@Overridepublic 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();
}
实例验证
接口调用
系统操作日志数据