SpringBoot实现操作日志记录的完整指南

 更新时间:2025年08月31日 09:24:58   作者:现在没有牛仔了  
在SpringBoot项目中,若将日志记录代码散乱地嵌入到每个业务方法的try-catch块中,会导致代码重复严重,不利于维护,本文将探讨如何利用SpringBoot面向切面编程(AOP) 来优雅地解决这一难题,希望对大家有所帮助

引言

在项目中,无论是在开发环境还是生产环境,如果系统出现故障,迎面而来的第一个问题往往是: “这个操作是谁在什么时候执行的?具体做了什么改动?” 如果系统对此一无所知,排查工作就如同大海捞针。

操作日志记录正是为了解决这一问题而生。它是一个系统性的、用于追踪用户行为、厘清操作责任以及复现历史流程的核心功能。它是系统的“黑匣子”,也是开发者的“记事本”。 在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操作日志记录内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现PIFrame窗体效果的示例代码

    Java实现PIFrame窗体效果的示例代码

    在很多现代应用中,常常需要使用个性化的窗体外观,摆脱传统窗口边框的限制,无边框、透明、圆角和阴影效果使得窗体显得更轻巧、更具视觉吸引力,同时允许用户自由拖拽和停靠窗体,所以本文给大家介绍了如何使用Java实现PIFrame窗体效果,需要的朋友可以参考下
    2025-03-03
  • springBoot service层事务控制的操作

    springBoot service层事务控制的操作

    这篇文章主要介绍了springBoot service层事务控制的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Spring @ExceptionHandler注解统一异常处理和获取方法名

    Spring @ExceptionHandler注解统一异常处理和获取方法名

    这篇文章主要介绍了Spring注解之@ExceptionHandler 统一异常处理和获取方法名,在实际项目中,合理使用@ExceptionHandler能够提高代码的可维护性和用户体验,通过本文的解析和实践,读者可以更好地理解和掌握@ExceptionHandler的用法和原理
    2023-09-09
  • SpringBoot异步导出文件的实现步骤

    SpringBoot异步导出文件的实现步骤

    本文主要介绍了SpringBoot异步导出文件的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-09-09
  • IDEA中如何去掉Java方法注释后的空行

    IDEA中如何去掉Java方法注释后的空行

    本文介绍了如何在IntelliJIDEA中去掉Java方法注释后多余的空行,并提供了详细的操作步骤,通过修改设置,可以避免默认格式化过程中自动插入的空行,使Javadoc注释更加紧凑和清晰,符合个人或团队的代码规范
    2025-02-02
  • Spring Data JPA使用JPQL与原生SQL进行查询的操作

    Spring Data JPA使用JPQL与原生SQL进行查询的操作

    这篇文章主要介绍了Spring Data JPA使用JPQL与原生SQL进行查询的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • springboot如何使用mongo做日志存储

    springboot如何使用mongo做日志存储

    这篇文章主要介绍了springboot如何使用mongo做日志存储方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Mybatis order by 动态传参出现的问题及解决方法

    Mybatis order by 动态传参出现的问题及解决方法

    今天,我正在愉快地CRUD,突然发现出现一个Bug,我们来看看是怎么回事吧!接下来通过本文给大家介绍Mybatis order by 动态传参出现的一个小bug,需要的朋友可以参考下
    2021-07-07
  • java正则表达式用法大全(深度好文)

    java正则表达式用法大全(深度好文)

    这篇文章主要给大家介绍了关于java正则表达式用法大全的相关资料,正则表达式在处理字符串时非常有用,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • Java实现带图形界面的聊天程序

    Java实现带图形界面的聊天程序

    这篇文章主要为大家详细介绍了Java实现带图形界面的聊天程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论