Spring Boot 操作日志记录与存储的常见实现方式

Spring Boot 操作日志记录与存储的常见实现方式

_

在 Spring Boot 项目中,操作日志(也称业务日志或审计日志)通常指记录用户的关键操作(如新增、修改、删除等),包括操作人、时间、模块、请求参数、结果等信息。这不同于系统日志(Logback 输出到文件/控制台)或实体审计(JPA 自动记录创建/修改时间)。

主流实现方式有两种:

  1. 使用 AOP + 自定义注解(最常见、最灵活,推荐用于业务操作日志)
  2. 使用 Spring Data JPA 的 Auditing(适合实体级审计,如自动记录创建/修改时间和操作人)

下面分别详细说明。

1. AOP + 自定义注解实现(记录业务操作日志,存储到数据库)

这种方式通过 AOP 拦截标注了自定义注解的方法,记录请求详情,并异步保存到数据库。适用于 Controller 或 Service 层的关键操作。

步骤:

  • 添加依赖(pom.xml):

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- 如果需要异步保存日志 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-async</artifactId>
    </dependency>
    
  • 设计操作日志表(示例 sys_oper_log):

    CREATE TABLE `sys_oper_log` (
      `id` bigint AUTO_INCREMENT PRIMARY KEY,
      `title` varchar(50) COMMENT '模块标题',
      `business_type` int DEFAULT 0 COMMENT '业务类型(0其它 1新增 2修改 3删除)',
      `method` varchar(255) COMMENT '方法名称',
      `request_method` varchar(10) COMMENT '请求方式',
      `operator_name` varchar(50) COMMENT '操作人',
      `oper_url` varchar(255) COMMENT '请求URL',
      `oper_ip` varchar(128) COMMENT '主机地址',
      `oper_param` varchar(2000) COMMENT '请求参数',
      `json_result` varchar(2000) COMMENT '返回参数',
      `status` int DEFAULT 0 COMMENT '操作状态(0正常 1异常)',
      `error_msg` varchar(2000) COMMENT '错误消息',
      `oper_time` datetime COMMENT '操作时间'
    );
    
  • 定义日志实体类(OperLog.java,使用 Lombok 简化):

    @Entity
    @Table(name = "sys_oper_log")
    public class OperLog {
        // 字段对应表结构,getter/setter 省略
    }
    
  • 自定义注解(Log.java):

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
        String title() default "";          // 模块标题
        int businessType() default 0;       // 业务类型
    }
    
  • AOP 切面类(LogAspect.java):

    @Aspect
    @Component
    public class LogAspect {
    
        @Autowired
        private OperLogService operLogService;  // 异步保存服务
    
        // 切点:拦截带有 @Log 注解的方法
        @Pointcut("@annotation(com.example.annotation.Log)")
        public void logPointCut() {}
    
        // 环绕通知(可记录请求前后)
        @Around("logPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            long beginTime = System.currentTimeMillis();
            Object result = point.proceed();  // 执行方法
            long time = System.currentTimeMillis() - beginTime;
    
            // 保存日志(异步)
            saveLog(point, time, null);
            return result;
        }
    
        // 异常通知
        @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
        public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
            saveLog((ProceedingJoinPoint) joinPoint, -1, e);
        }
    
        private void saveLog(ProceedingJoinPoint joinPoint, long time, Throwable e) {
            // 获取注解信息、请求信息、参数等
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Log logAnno = signature.getMethod().getAnnotation(Log.class);
    
            OperLog operLog = new OperLog();
            operLog.setTitle(logAnno.title());
            operLog.setBusinessType(logAnno.businessType());
            // ... 填充其他字段:操作人(从 SecurityContext 获取)、IP、参数、结果、异常等
    
            if (e != null) {
                operLog.setStatus(1);
                operLog.setErrorMsg(e.getMessage());
            }
            operLog.setOperTime(new Date());
    
            // 异步保存(避免影响主业务性能)
            operLogService.saveAsync(operLog);
        }
    }
    
  • 使用方式(在 Controller 方法上加注解):

    @PostMapping("/add")
    @Log(title = "用户管理", businessType = 1)  // 1 表示新增
    public Result addUser(@RequestBody User user) {
        // 业务代码
    }
    

优点:灵活、可记录详细业务信息(如参数前后对比)。
注意:日志量大时建议异步保存、分表或存 Elasticsearch。

2. Spring Data JPA Auditing(实体级审计日志)

适合自动记录实体的创建/修改时间和操作人。

  • 启用 Auditing(在启动类上):

    @SpringBootApplication
    @EnableJpaAuditing  // 启用 JPA Auditing
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        // 如果需要记录操作人,提供 AuditorAware Bean
        @Bean
        public AuditorAware<String> auditorProvider() {
            return () -> Optional.of(SecurityContextHolder.getContext().getAuthentication().getName());
        }
    }
    
  • 基类实体(AuditableEntity.java):

    @MappedSuperclass
    @EntityListeners(AuditingEntityListener.class)
    public abstract class AuditableEntity {
    
        @CreatedDate
        @Column(updatable = false)
        private LocalDateTime createdDate;
    
        @LastModifiedDate
        private LocalDateTime lastModifiedDate;
    
        @CreatedBy
        @Column(updatable = false)
        private String createdBy;
    
        @LastModifiedBy
        private String lastModifiedBy;
    }
    
  • 实体继承

    @Entity
    public class User extends AuditableEntity {
        // 其他字段
    }
    

优点:零侵入,自动填充。
缺点:仅限于实体 CRUD,不适合复杂业务操作日志。

总结与建议

  • 如果是业务操作日志(谁操作了什么接口、参数是什么),优先用 AOP + 自定义注解,存储到专用日志表或 ES。
  • 如果是实体变更审计(创建/修改时间、人),用 Spring Data JPA Auditing
  • 生产环境:日志异步存储,避免影响性能;结合 Spring Security 获取当前用户。
如何获取操作人IP? 2025-12-15

评论区