简介
一次偶然的机会,在做一个系统的时候需要记录用户操作日志,以下分享下。
步骤
1、依赖
需要引入相应依赖,这里肯定时能和数据库关联了(简单的来说就是能够在浏览器上进行增删改查)。
<!-- aop的依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2、自定义日志注解
定义一个方法级别的@Log注解,用于标注需要监控的方法:
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** 定义一个Log的日志注解,用在方法上*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Log {String value() default "";}
3、创建库表和实体
在数据库中创建一张sys_operation_log表,用于保存用户的操作日志
a. 数据库
SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for sys_operation_log-- ----------------------------DROP TABLE IF EXISTS `sys_operation_log`;CREATE TABLE `sys_operation_log` (`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户操作日志主键',`LOG_NAME` varchar(255) DEFAULT NULL COMMENT '日志名称',`USER_ID` varchar(30) DEFAULT NULL COMMENT '用户id',`API` varchar(255) DEFAULT NULL COMMENT 'api名称',`METHOD` varchar(255) DEFAULT NULL COMMENT '方法名称',`CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',`SUCCEED` tinyint(4) DEFAULT NULL COMMENT '是否执行成功(0失败1成功)',`MESSAGE` varchar(255) DEFAULT NULL COMMENT '具体消息备注',PRIMARY KEY (`ID`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='操作日志';-- ------------------------------ Records of sys_operation_log-- ----------------------------INSERT INTO `sys_operation_log` VALUES ('17', '业务操作日志', 'tom', '/resource/menus', 'GET', '2018-04-22 16:05:05', '1', null);INSERT INTO `sys_operation_log` VALUES ('18', '业务操作日志', 'tom', '/resource/menus', 'GET', '2018-04-22 16:05:09', '1', null);INSERT INTO `sys_operation_log` VALUES ('19', '业务操作日志', 'tom', '/resource/api/-1/1/10', 'GET', '2018-04-22 16:08:15', '1', null);
b. 实体类
import com.fasterxml.jackson.annotation.JsonFormat;import lombok.Data;import java.io.Serializable;import java.util.Date;/*** 用户操作日志主键* Create by ck on 2019/6/17*/@Datapublic class sysOperationLog implements Serializable {private static final long serialVersionUID = 8147899229715719433L;/*** 主键*/private Integer id;/*** 日志名称*/private String logName;/*** 用户id*/private String userId;/*** api名称*/private String api;/*** 方法名称*/private String method;/*** 是否执行成功 0失败 -- 1成功*/private Byte succeed;/*** 详细备注*/private String message;/*** 创建时间*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createTime;}
4、切面和切点
定义一个LogAspect类,使用@Aspect标注让其成为一个切面,切点为使用@Log注解标注的方法,使用@Around环绕通知:
import com.ck.syscheck.annotate.Log;import com.ck.syscheck.model.SysOperationLog;import com.ck.syscheck.service.SysOperationService;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.LocalVariableTableParameterNameDiscoverer;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.Date;@Aspect@Componentpublic class LogAspect {@Autowiredprivate SysOperationService sysOperationService;@Pointcut("@annotation(com.ck.syscheck.annotate.Log)")public void pointcut() { }@Around("pointcut()")public Object around(ProceedingJoinPoint point) {Object result = null;long beginTime = System.currentTimeMillis();try {// 执行方法result = point.proceed();} catch (Throwable e) {e.printStackTrace();}// 执行时长(毫秒)long time = System.currentTimeMillis() - beginTime;// 保存日志saveLog(point, time);return result;}private void saveLog(ProceedingJoinPoint joinPoint, long time) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();SysOperationLog sysOperationLog = new SysOperationLog();Log logAnnotation = method.getAnnotation(Log.class);if (logAnnotation != null) {// 注解上的描述sysOperationLog.setApi(logAnnotation.value());}// 请求的方法名String className = joinPoint.getTarget().getClass().getName();String methodName = signature.getName();sysOperationLog.setMethod(className + "." + methodName + "()");// 请求的方法参数值Object[] args = joinPoint.getArgs();// 请求的方法参数名称LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();String[] paramNames = u.getParameterNames(method);if (args != null && paramNames != null) {String params = "";for (int i = 0; i < args.length; i++) {params += " " + paramNames[i] + ": " + args[i];}sysOperationLog.setMessage(params);}// 模拟一个用户名sysOperationLog.setUserId("admin");sysOperationLog.setCreateTime(new Date());// 保存系统日志sysOperationService.insert(sysOperationLog);}}
5、测试
import com.ck.syscheck.annotate.Log;import org.springframework.web.servlet.ModelAndView;@RestController@RequestMapping("/api")public class SysUserController {/*** @return 测试*/@Log("测试test")@RequestMapping("/test")public ModelAndView test(ModelAndView modelAndView) {modelAndView.setViewName("/front/test");return modelAndView;}@Log("添加add")@RequestMapping("/add")public ModelAndView add(ModelAndView modelAndView) {modelAndView.setViewName("/front/add");return modelAndView;}@Log("更新Update")@RequestMapping("/update")public ModelAndView update(ModelAndView modelAndView) {modelAndView.setViewName("/front/update");return modelAndView;}}
可以存到数据库
这里面是以部分字段作为参考栗子,具体的还需要看项目需求。demo源码所在位置:
GitHub:https://github.com/Tidetrace/sys-check
