在 Spring Boot 项目中,操作日志(也称业务日志或审计日志)通常指记录用户的关键操作(如新增、修改、删除等),包括操作人、时间、模块、请求参数、结果等信息。这不同于系统日志(Logback 输出到文件/控制台)或实体审计(JPA 自动记录创建/修改时间)。
主流实现方式有两种:
- 使用 AOP + 自定义注解(最常见、最灵活,推荐用于业务操作日志)
- 使用 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 获取当前用户。