持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
使用Aop实现操作日志记录
1. 创建一个注解用来做日志记录
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysOperaLog {
String value() default "";
}
2. 准备Aop 操作类
package com.xk.bugvip.base.aop.log;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.xk.bugvip.base.aop.log.event.SysLogEvent;
import com.xk.bugvip.entity.SysLog;
import com.xk.bugvip.utils.R;
import com.xk.bugvip.utils.constant.LogConstant;
import com.xk.bugvip.utils.util.IpData;
import com.xk.bugvip.utils.util.IpGetAdders;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;
@Aspect
@Order
@Component
public class LogAspect {
private ThreadLocal<SysLog> sysLogThreadLocal = new ThreadLocal<>();
/**
* 事件发布是由ApplicationContext对象管控的,
* 我们发布事件前需要注入ApplicationContext对象调用publishEvent方法完成事件发布
**/
@Resource
private ApplicationContext applicationContext;
/**
* 业务操作断点位置 定义controller 切入点拦截规则 ,拦截SysLog注解得方法
*/
@Pointcut("@annotation(com.xk.bugvip.base.aop.log.SysOperaLog)")
public void sysLogAspect(){
}
/**
* 拦截控制器得操作日志
* @param joinpoint
* @throws Throwable
*/
@Before(value = "sysLogAspect()")
public void recordLog(JoinPoint joinpoint ) throws Throwable{
SysLog sysLog =new SysLog();
//将当前实体保存到threadLoacl
sysLogThreadLocal.set(sysLog);
//开始时间
long beginTime = Instant.now().toEpochMilli();
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
sysLog.setActionUrl(URLUtil.getPath(request.getRequestURI()));
sysLog.setStartTime(LocalDateTime.now().toString());
//todo 设置操作人
// sysLog.setUserName();
//设置ip
String ip = ServletUtil.getClientIP(request);
sysLog.setIp(ip);
//设置地址
IpData ipData = IpGetAdders.doPostOrGet();
sysLog.setLocation(ipData.getCname());
sysLog.setRequestMethod(request.getMethod());
//设置浏览器
String header = request.getHeader("User-Agent");
sysLog.setBrowser(UserAgentUtil.parse(header).getBrowser().toString());
//设置操作系统
sysLog.setOs(UserAgentUtil.parse(header).getOs().toString());
//访问目标方法的参数 可动态改变参数值
Object[] args=joinpoint.getArgs();
//获取执行的方法名
sysLog.setActionMethod(joinpoint.getSignature().getName());
//类名
sysLog.setClassPath(joinpoint.getTarget().getClass().getName());
sysLog.setActionMethod(joinpoint.getSignature().getName());
sysLog.setParams(Arrays.toString(args));
String controllerMethodDescription = LogUtil.getControllerMethodDescription(joinpoint);
sysLog.setDescription(controllerMethodDescription);
long endTime = Instant.now().toEpochMilli();
sysLog.setConsumingTime(endTime-beginTime);
}
/**
* 返回通知
* @param ret
*/
@AfterReturning(returning = "ret",pointcut = "sysLogAspect()")
public void doAfterReturning(Object ret) {
SysLog sysLog = sysLogThreadLocal.get();
//处理完请求 返回内容
//处理添加日志
sysLog.setRunType(LogConstant.LOG_INFO);
sysLog.setLogType(LogConstant.OPERATION_LOG);
sysLog.setFinishTime(LocalDateTime.now().toString());
sysLog.setStatus(1);
//sysLog.setReturnData(ret.toString());
//发布事件
applicationContext.publishEvent(new SysLogEvent(sysLog));
//移除当前log实体
sysLogThreadLocal.remove();
}
/**
* 异常通知
* @param e
*/
@AfterThrowing(pointcut = "sysLogAspect()", throwing = "e")
public void doAfterThrowable(Throwable e){
SysLog sysLog = sysLogThreadLocal.get();
//异常
sysLog.setRunType(LogConstant.LOG_THORW);
sysLog.setLogType(LogConstant.OPERATION_LOG);
//异常对象
sysLog.setExDetail(LogUtil.getStackTrace(e));
//异常信息
sysLog.setExDesc(e.getMessage());
sysLog.setStatus(2);
sysLog.setReturnData(e.toString());
//发布事件
applicationContext.publishEvent(new SysLogEvent(sysLog));
//移除当前log实体
sysLogThreadLocal.remove();
}
}
3 另外我们这边用到了 ApplicationEvent
package com.xk.bugvip.base.aop.log.event;
import org.springframework.context.ApplicationEvent;
/**
* @Description 系统日志事件
* ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、
* 订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,
* 提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,
* 发布者的工作只是为了发布事件而已。
*
*/
public class SysLogEvent extends ApplicationEvent {
/**
* 我们自定义事件SysLogEvent继承了ApplicationEvent,继承后必须重载构造函数,构造函数的参数可以任意指定,
* 其中source参数指的是发生事件的对象,一般我们在发布事件时使用的是this关键字代替本类对象,
* 而sysLog参数是我们自定义的注册用户对象,该对象可以在监听内被获取。
* @param source
*/
public SysLogEvent(Object source) {
super(source);
}
}
4 创建监听类并交给容器管理 在AOP更新日志时 publishEvent会被监测到
@Slf4j
@Component
public class SysLogListener {
@Autowired
private ILogService sysLogService;
/*
让监听类被Spring所管理即可,在我们用户注册监听实现方法上添加@EventListener注解,
该注解会根据方法内配置的事件完成监听。
*/
@Async
@Order
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event){
SysLog sysLog=(SysLog) event.getSource();
//保存日志
sysLogService.save(sysLog);
}
}
5 使用 @SysOperaLog注解 在controller上
@PostMapping("/getlist")
@SysOperaLog("获取日志列表")
public R getlist(@RequestBody ListVo parm){
return iLogService.getList(parm);
}
实践是检验真理的唯一方法! 明天见🥰🥰🥰