一、前言
我们写完一个项目,运维时,如果出现了bug,我们需要查看控制台的日志,但是那个日志无关方法太多,查找不是很方便,还有就是一个项目上线之后,我们需要记录谁操作了那些功能,以防出现矛盾知道是谁点了这个功能造成的问题,由谁来负责,为了解决这两个问题,我在SpringBoot项目中使用了对控制层切面+注解的方法来实现将日志存储在数据库里面
二、实现详细源码
1、相关依赖
springboot项目的依赖需要,还需要一个aop切面的依赖,mybatis的依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
2、配置文件
在application.properties文件里加这样一条配置
server.port: 8080 spring.aop.auto=true
3、我们首先需要准备mysql表
DROP TABLE IF EXISTS `syslog`; CREATE TABLE `syslog` ( `id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键', `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名', `operation` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作', `method` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '方法名', `createDate` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '日志' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 1;
4、创建数据库表之后就需要写实体类了
package com.sgsg.verification.entity; /** * @author 王恒杰 * @date 2022/10/25 14:54 * @Description: */ import java.io.Serializable; public class Syslog implements Serializable { private String id; //我用的UUIT private String username; //用户名 private String operation; //操作 private String method; //方法名 private String createDate; //操作时间,这里可以使用Date来实现。我写的有个工具类。用的String接收 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getOperation() { return operation; } public void setOperation(String operation) { this.operation = operation; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public String getCreateDate() { return createDate; } public void setCreateDate(String createDate) { this.createDate = createDate; } }
这里如果用lombok的话,只需要一个@Data注解
@Data public class Syslog implements Serializable { private String id; //我用的全宇宙唯一的子串串、也是直接用的工具类 private String username; //用户名 private String operation; //操作 private String method; //方法名 private String createDate; //操作时间,这里可以使用Date来实现。我写的有个工具类。用的String接收 }
5、先写dao层和mapper层
(1)dao层
/** * @author 王恒杰 * @date 2022/10/25 14:59 * @Description: */ @Mapper public interface SysLogMapper { /** * 写入日志 * @param syslog * @return */ int addLog(Syslog syslog); }
(2)mapper层
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.sgsg.verification.dao.SysLogMapper"> <insert id="addLog" parameterType="com.sgsg.verification.entity.Syslog"> insert into syslog(id, username, operation, method, createDate) values (#{id}, #{username}, #{operation}, #{method}, #{createDate}) </insert> </mapper>
6、写业务层
接口层
import com.sgsg.verification.entity.Syslog; /** * @author 王恒杰 * @date 2022/10/25 14:58 * @Description: */ public interface SysLogService { //写入日志 int addLog(Syslog syslog); }
实现类
/** * @author 王恒杰 * @date 2022/10/25 14:58 * @Description: */ @Service public class SysLogServiceImpl implements SysLogService { @Autowired public SysLogMapper sysLogMapper; //写入日志 @Override public int addLog(Syslog syslog) { return sysLogMapper.addLog(syslog); } }
7、切面核心方法
package com.sgsg.verification.aspect; /** * @author 王恒杰 * @date 2022/10/25 14:57 * @Description: */ import com.auth0.jwt.interfaces.Claim; import com.sgsg.verification.dao.UserDao; import com.sgsg.verification.entity.Result; import com.sgsg.verification.entity.Syslog; import com.sgsg.verification.entity.UserInfoEntity; import com.sgsg.verification.log.Mylog; import com.sgsg.verification.service.SysLogService; import com.sgsg.verification.service.UserService; import com.sgsg.verification.utils.JwtUtil; import com.sgsg.verification.utils.UserInfoUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; 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.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.UUID; /** * 系统日志:切面处理类 */ @Aspect @Component public class SysLogAspect { @Autowired private SysLogService sysLogService;//将数据写入数据库的操作 @Autowired private UserDao userDao; @Autowired private JwtUtil jwtUtil; //定义切点 @Pointcut //在注解的位置切入代码 @Pointcut("@annotation(com.sgsg.verification.log.Mylog ))") public void logPoinCut() { } //切面 配置通知 @AfterReturning("logPoinCut()") public void saveSysLog(JoinPoint joinPoint) { System.out.println("切面。。。。。"); //保存日志 Syslog sysLog = new Syslog(); //从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //获取切入点所在的方法 Method method = signature.getMethod(); //获取操作 Mylog myLog = method.getAnnotation(Mylog.class); if (myLog != null) { String value = myLog.value(); sysLog.setOperation(value);//保存获取的操作 } //设置id String id = UUID.randomUUID().toString(); sysLog.setId(id); //获取请求的类名 String className = joinPoint.getTarget().getClass().getName(); //获取请求的方法名 String methodName = method.getName(); sysLog.setMethod(className + "." + methodName); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日"); sysLog.setCreateDate(simpleDateFormat.format(new Date())); //获取用户名 //拿到当前用户的信息、我这里使用的Token。直接从Token中获取当前用户信息 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert attributes != null; HttpServletRequest request = attributes.getRequest(); // 从 http 请求头中取出 token String token = request.getHeader("authorization"); // 解析token并获取token中的用户信息 Map<String, Claim> claims = jwtUtil.verity(token); Claim userId = claims.get("userId"); //获取用户 UserInfoEntity user = userDao.getInfoById(userId.asInt()); sysLog.setUsername(user.getCname()); //调用service保存SysLog实体类到数据库 sysLogService.addLog(sysLog); } }
8、我们自己定义注解类
package com.sgsg.verification.log; /** * @author 王恒杰 * @date 2022/10/25 14:56 * @Description: 自定义注解类 */ import java.lang.annotation.*; @Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上 @Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行 @Documented //生成文档 public @interface Mylog { String value() default ""; }
9、在控制层使用我们自定义的注解
package com.sgsg.verification.controller; import com.sgsg.verification.entity.Result; import com.sgsg.verification.log.Mylog; import com.sgsg.verification.service.EnumService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 王恒杰 * @date 2022/10/6 12:30 * @Description: */ @Slf4j @RestController @RequestMapping("/enums") public class EnumController { @Autowired private EnumService enumService; @Mylog(value = "获取枚举") @GetMapping("/selectAll") public Result selectAll() { return enumService.selectAll(); } }
10、启动项目之后我们的日志就存储在数据库里面了