SpringBoot实现操作日志记录的完整指南
作者:现在没有牛仔了
引言
在项目中,无论是在开发环境还是生产环境,如果系统出现故障,迎面而来的第一个问题往往是: “这个操作是谁在什么时候执行的?具体做了什么改动?” 如果系统对此一无所知,排查工作就如同大海捞针。
操作日志记录正是为了解决这一问题而生。它是一个系统性的、用于追踪用户行为、厘清操作责任以及复现历史流程的核心功能。它是系统的“黑匣子”,也是开发者的“记事本”。 在SpringBoot项目中,若将日志记录代码散乱地嵌入到每个业务方法的try-catch块中,会导致代码重复严重、核心业务逻辑被污染、不利于维护。
本文将探讨如何利用SpringBoot面向切面编程(AOP) 来优雅地解决这一难题。
一、环境准备与项目搭建
1. 创建一个标准的SpringBoot项目
我使用的是SpringBoot3.5.5+JDK17
2. 引入核心依赖
- mysql-connector-j(数据库驱动)
- mybatis-plus-spring-boot3-starter(数据持久化框架)
- spring-boot-starter-aop(AOP核心)
- aspectjweaver(面向切面工具)
- spring-boot-starter-web(Web项目)
- fastjson2(阿里json解析器)
二、实现步骤
1. 创建日志实体类(OperateLog)
@Data public class OperateLog { private Long id; //主键 private Long userId; //操作人id private Date createTime; //创建时间 private String description; //请求描述 private String method; //请求方法 如:post、get private String ip; //操作人ip private String param; //请求参数:json格式 private String result; //请求结果:json格式 private Integer success; //操作是否成功 private String errorMsg; //错误信息 }
2. 创建操作日志记录表(operate_log)
create table operate_log( id BIGINT PRIMARY key AUTO_INCREMENT COMMENT '主键', user_id BIGINT DEFAULT 100 COMMENT '操作人id', create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', description VARCHAR(200) COMMENT '请求描述', method VARCHAR(20) COMMENT '请求方法 如:post、get', ip VARCHAR(32) COMMENT '操作人ip', param VARCHAR(2000) COMMENT '请求参数:json格式', result VARCHAR(2000) COMMENT '请求结果:json格式', success TINYINT DEFAULT 0 COMMENT '操作是否成功:1是、0否', error_msg VARCHAR(2000) COMMENT '错误消息' ) COMMENT '操作日志记录表';
3. 创建添加日志的相关代码
//mapper接口 public interface OperateLogMapper extends BaseMapper<OperateLog> {} //service接口 public interface OperateLogService extends IService<OperateLog> {} //service实现类 @Service public class OperateLogServiceImpl extends ServiceImpl<OperateLogMapper, OperateLog> implements OperateLogService {}
4. 创建自定义注解(@Log)
在注解类中,指定其可以对方法进行修饰,并设置运行时保留策略
@Target({ ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** * 请求描述 */ String title() default ""; }
5. 创建切面类(LogAspect)- 核心
在切面类中,创建前置通知和后置通知,并指定被@Log
注解修饰的方法为切点,实现对目标方法的“增强”。
@Aspect @Component public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); @Autowired private OperateLogService logService; private final ExecutorService executor = Executors.newSingleThreadExecutor(); /** * 请求前执行 * @param aspLog */ @Before(value = "@annotation(aspLog)") public void doBefore(Log aspLog){ log.info("请求日志记录start"); } /** * 请求后执行 * @param aspLog * @param result */ @AfterReturning(pointcut = "@annotation(aspLog)",returning = "result") public void doAfterReturning(JoinPoint joinPoint, Log aspLog, Object result){ handleLog(joinPoint,aspLog,null,result); } /** * 请求异常执行 * @param aspLog * @param e */ @AfterThrowing(value = "@annotation(aspLog)", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint,Log aspLog,Exception e){ handleLog(joinPoint,aspLog,e,null); } /** * 日志处理器-记录日志 * @param aspLog * @param e * @param result */ private void handleLog(JoinPoint joinPoint,Log aspLog,Exception e,Object result){ ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); OperateLog operateLog = new OperateLog(); operateLog.setIp(attributes.getRequest().getRemoteAddr()); operateLog.setMethod(attributes.getRequest().getMethod()); String param = JSON.toJSONString(joinPoint.getArgs()); operateLog.setParam(param); if(e != null){ operateLog.setErrorMsg(e.getLocalizedMessage()); }else{ operateLog.setSuccess(1); operateLog.setResult(JSON.toJSONString(result)); } operateLog.setDescription(aspLog.title()); executor.submit(() -> logService.save(operateLog)); } }
6. 编写控制层代码进行测试
@RestController public class TestController { @Log(title = "测试操作") @GetMapping("/test") public String test(){ return "没毛病"; } @Log(title = "测试参数操作") @PostMapping("/testParam") public String testParam(@RequestBody String data){ return data; } @Log(title = "测试异常操作") @GetMapping("/testExecption") public String testExecption(){ return (1/0)+""; } }
7. 其他注意事项
如果你使用的Springboot也是3.0+,在引入mybatis-plus依赖的时候,一定要导入mybatis-plus-spring-boot3-starter
,而不是mybatis-plus-boot-starter
。如果导入的依赖不正确,会导致Spring无法注入mybatis相关的内容并报错。
三、功能测试与验证(Postman)
测试三种情况,1、测试无参get请求,2、测试传参post请求,3、测试异常请求,对于成功的请求要在日志记录中标记为成功,对于异常的请求要记录异常信息。
1. 测试无参get请求
2. 测试post带参数请求
3. 测试异常请求
四、 总结
到这里,就完成了操作日志的记录功能,需要注意的是,在日常开发中,一定要将记录日志的代码交给另一个线程执行,避免持久化操作对主线程产生性能上的影响。面向切面的本质上是对切点方法做了一层代理,在不影响业务代码的前提下对其进行功能扩展,降低了业务代码和非业务代码的耦合度,这种设计适用于很多其他的业务场景,比如异常处理、接口性能监控、参数校验与预处理等。
到此这篇关于SpringBoot实现操作日志记录的完整指南的文章就介绍到这了,更多相关SpringBoot操作日志记录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!