天天看点

让你一篇文章搞明白springboot项目记录操作日志的所有知识点

作者:风趣的咕噜咕噜

springboot中记录操作日志的方式有哪些呢?他们各自优缺点你了解吗?这个问题不仅工作中常常会用到,而且面试中也是高频出现的问题,今天就自己多年工作经验总结下。

1.利用 AOP 切面拦截请求:通过在切面中拦截请求,获取请求相关的信息(如请求 URL、请求参数、用户信息等),并将这些信息记录到操作日志中。这种方式比较常见,也比较灵活,可以记录所有的请求操作。

2.利用 Filter 过滤器拦截请求:与 AOP 类似,通过在过滤器中拦截请求,获取请求相关信息,并将这些信息记录到操作日志中。但相比 AOP,Filter 更为底层,能够获取更多的请求信息。

3.利用注解方式记录操作日志:通过在方法上添加注解,在方法执行前后获取请求相关的信息,并将这些信息记录到操作日志中。这种方式比较简单,但需要手动添加注解,对于已经存在的代码需要修改,比较繁琐。

4.利用拦截器拦截请求:与 AOP、Filter 类似,拦截器也可以拦截请求,获取请求相关信息,并将这些信息记录到操作日志中。不同的是,拦截器是 Spring MVC 中的组件,因此可以很方便地获取请求和响应相关的信息。

5.利用 Spring Boot 的 Actuator 模块记录操作日志:Actuator 是 Spring Boot 提供的一组监控和管理功能,可以方便地查看应用程序的运行状态和指标数据。其中,AuditingEndpoint 和 HttpTraceEndpoint 组件可以记录应用程序的审计和 HTTP 请求相关信息,包括操作日志。

让你一篇文章搞明白springboot项目记录操作日志的所有知识点

下面逐一介绍下具体实现流程

一、“利用 AOP 切面拦截请求”这种方式具体实现流程和代码

  • 定义一个切面类来实现记录操作日志的逻辑:
@Aspect
@Component
public class LogAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private LogService logService;

    @Pointcut("execution(public * com.example.demo.controller..*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        // 在方法执行前记录日志
        // 获取请求的方法名和参数
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        // 将日志写入数据库
        Log log = new Log();
        log.setOperation(methodName);
        log.setParams(Arrays.toString(args));
        logService.addLog(log);
        logger.info("日志记录成功");
    }

    @AfterReturning(returning = "result", pointcut = "webLog()")
    public void doAfterReturning(Object result) {
        // 在方法执行后记录日志
        logger.info("返回值: " + result);
    }

}
           
  • 定义一个日志实体类:
public class Log {

    private Long id;
    private String operation;
    private String params;
    private Date createTime;

    // 省略 getter 和 setter 方法
}
           
  • 定义一个记录日志的服务接口和实现类:
public interface LogService {

    void addLog(Log log);

}

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    @Override
    public void addLog(Log log) {
        log.setCreateTime(new Date());
        logDao.add(log);
    }

}
           
  • 配置 AOP 切面:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
           
  • 在日志配置文件中配置日志输出:
<logger name="com.example.demo.aspect.LogAspect" level="INFO"/>
           

这样,每当请求经过被 @Controller 或 @RestController 注解的方法时,LogAspect 切面类中的 doBefore() 方法就会被执行,将请求的方法名和参数记录到数据库中。在方法执行后,doAfterReturning() 方法将会记录返回值。通过这种方式,可以方便地记录操作日志。

二、“利用 Filter 过滤器拦截请求”这种方式具体实现流程和代码

使用 Filter 过滤器记录操作日志的方式实现流程如下:

  • 编写 Filter 类

编写一个实现 javax.servlet.Filter 接口的 Filter 类,重写其 doFilter 方法,在该方法中实现日志记录逻辑。

@Component
public class LogFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 从 servletRequest 中获取请求信息
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String url = request.getRequestURI(); // 请求的 URL
        String method = request.getMethod(); // 请求的方法
        String queryString = request.getQueryString(); // 请求的查询字符串

        // 执行目标资源,即进入下一个过滤器或 Servlet
        filterChain.doFilter(servletRequest, servletResponse);

        // 从 servletResponse 中获取响应信息
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        int status = response.getStatus(); // 响应的状态码

        // 记录日志
        log.info("请求 {} {} {},响应状态码为 {}", method, url, queryString, status);
      	//保存数据库的操作省了
      
    }
}
           
  • 在配置类中注册 Filter

在 Spring Boot 应用的配置类中注册 Filter:

javaCopy code@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean<LogFilter> logFilter() {
        FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LogFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("LogFilter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
}
           

在该配置类中创建 FilterRegistrationBean 对象,并将 LogFilter 类型的 Bean 注入到其中。通过设置 addUrlPatterns 方法设置需要拦截的 URL 模式,通过 setName 方法设置 Filter 的名称,通过 setOrder 方法设置 Filter 的执行顺序。

通过以上两步即可实现 Filter 方式记录操作日志的功能。在应用启动后,每次发起请求时,Filter 将会记录该请求的相关信息,并输出到日志中。

三、“利用注解方式记录操作日志”这种方式具体实现流程和代码

利用注解方式记录操作日志的实现步骤如下:

  • 定义注解:首先需要定义一个注解,用于标识需要记录操作日志的方法。可以定义在方法上或者类上,具体根据需求而定。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
    String value() default "";
}
           
  • 实现注解处理器:定义一个注解处理器,用于处理被@LogAnnotation注解标记的方法或类,并记录操作日志。
@Component
@Aspect
public class LogAspect {

    @Autowired
    private LogService logService;

    @Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取注解内容
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
        String operation = logAnnotation.value();

        // 记录操作日志
        Log log = new Log();
        log.setOperation(operation);
        logService.save(log);

        // 执行原方法
        Object result = point.proceed();

        return result;
    }
}
           
  • 在需要记录操作日志的方法上添加注解:在需要记录操作日志的方法上添加@LogAnnotation注解,并设置注解内容。
@LogAnnotation("登录")
public void login(String username, String password) {
    // do something
}
           

通过以上步骤,即可实现利用注解方式记录操作日志。

需要注意的是,使用注解方式记录操作日志时,需要将注解处理器添加到Spring容器中,并且需要在AOP配置文件中添加对该处理器的配置。具体代码可以参考以下示例:

@Configuration
public class AppConfig {

    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
           

以上示例中,AppConfig类用于将注解处理器添加到Spring容器中,AopConfig类用于开启AOP支持并添加对注解处理器的配置。

四、“利用拦截器拦截请求”这种方式具体实现流程和代码

利用拦截器拦截请求记录操作日志的实现流程和代码如下:

  • 定义一个实现了 HandlerInterceptor 接口的拦截器类,实现 preHandle() 方法和 afterCompletion() 方法。其中,preHandle() 方法会在请求处理之前执行,afterCompletion() 方法会在请求处理完成之后执行。
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理之前记录日志
        String url = request.getRequestURI();
        String method = request.getMethod();
        String params = request.getQueryString();
        String ip = request.getRemoteAddr();
        // ...
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在请求处理完成之后记录日志
        String url = request.getRequestURI();
        String method = request.getMethod();
        String params = request.getQueryString();
        String ip = request.getRemoteAddr();
        // ...
    }
}
           
  • 在 Spring Boot 应用程序的配置类中注册拦截器。
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LogInterceptor logInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor);
    }
}
           
  • 为需要记录操作日志的请求方法添加注解,例如 @LogAnnotation。
@GetMapping("/user/{id}")
@LogAnnotation("查询用户信息")
public User getUser(@PathVariable Long id) {
    return userService.getUserById(id);
}
           
  • 定义一个注解解析器,用于解析 @LogAnnotation 注解中的信息并在拦截器中记录日志。
@Aspect
@Component
public class LogAnnotationAspect {
    @Autowired
    private LogInterceptor logInterceptor;

    @Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
    public void logPointCut() {}

    @Before("logPointCut() && @annotation(logAnnotation)")
    public void before(JoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        logInterceptor.logBefore(request, joinPoint, logAnnotation);
    }

    @AfterReturning(returning = "ret", pointcut = "logPointCut() && @annotation(logAnnotation)")
    public void afterReturning(Object ret, LogAnnotation logAnnotation) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        logInterceptor.logAfter(request, logAnnotation);
    }
}
           
  • 在拦截器中记录日志。
@Component
public class LogInterceptor {
    public void logBefore(HttpServletRequest request, JoinPoint joinPoint, LogAnnotation logAnnotation) {
        // 在请求处理之前记录日志
        String url = request.getRequestURI();
        String method = request.getMethod();
        String params = Arrays.toString(joinPoint.getArgs());
        String ip = request.getRemoteAddr();
        String operation = logAnnotation.value();
        // ...
    }

    public void logAfter(HttpServletRequest request, LogAnnotation logAnnotation) {
        // 在请求处理完成之后记录日志
        String url = request.getRequestURI();
        String method = request.getMethod();
        String ip = request.getRemoteAddr();
        String operation = logAnnotation.value();
        // ...
    }
}           

五、“利用 Spring Boot 的 Actuator 模块记录操作日志”这种方式具体实现流程和代码

利用 Spring Boot 的 Actuator 模块记录操作日志可以通过集成 Actuator 模块中的 Audit 功能来实现。

  • 集成 Actuator 模块

在 pom.xml 文件中添加 Actuator 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
           
  • 配置 Audit

在 application.yml 或 application.properties 文件中添加 Audit 相关配置:

management:
  audit:
    enabled: true
    record-data: true
           

其中 enabled 属性表示是否启用 Audit,record-data 属性表示是否记录请求和响应数据。

  • 编写 Controller

在需要记录操作日志的 Controller 方法上添加 @Audit 注解:

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @Audit("查询用户列表")
    @GetMapping("/users")
    public List<User> getUsers() {
        return userService.getUsers();
    }

}
           
  • 测试

启动应用程序,然后访问 http://localhost:8080/actuator/auditevents,可以看到 Audit 记录的事件列表。其中,data.request 和 data.response 字段记录了请求和响应数据。

注意:需要启用 Spring Security 来保护 Actuator 端点,避免未授权访问 Audit 数据。

spring:
  security:
    user:
      name: admin
      password: password

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    audit:
      enabled: true
      sensitive: true
  security:
    roles: ACTUATOR_ADMIN
           

以上就是利用 Spring Boot 的 Actuator 模块记录操作日志的具体实现流程和代码。

下面总结下每种方式的优缺点

让你一篇文章搞明白springboot项目记录操作日志的所有知识点

1、利用 AOP 切面拦截请求

优点:

可以精细地控制需要记录的操作和不需要记录的操作;

可以统一处理异常,避免记录操作日志时出现异常而中断;

可以通过注解方式方便地对不同的操作类型进行区分。

缺点:

会对系统性能造成一定的影响;

需要熟悉 AOP 的使用方法。

2、利用 Filter 过滤器拦截请求

优点:

可以拦截所有请求,无需配置;

可以统一处理异常,避免记录操作日志时出现异常而中断;

可以通过注解方式方便地对不同的操作类型进行区分。

缺点:

不能精细地控制需要记录的操作和不需要记录的操作;

会对系统性能造成一定的影响;

需要熟悉 Filter 的使用方法。

3、利用注解方式记录操作日志

优点:

可以精细地控制需要记录的操作和不需要记录的操作;

可以通过注解方式方便地对不同的操作类型进行区分。

缺点:

需要在每个需要记录操作日志的方法上添加注解,工作量较大;

如果有很多需要记录操作日志的方法,容易造成代码冗余。

4、利用拦截器拦截请求

优点:

可以拦截所有请求,无需配置;

可以统一处理异常,避免记录操作日志时出现异常而中断;

可以通过注解方式方便地对不同的操作类型进行区分。

缺点:

不能精细地控制需要记录的操作和不需要记录的操作;

会对系统性能造成一定的影响;

需要熟悉拦截器的使用方法。

5、利用 Spring Boot 的 Actuator 模块记录操作日志

优点:

可以快速地集成到 Spring Boot 项目中,无需编写额外的代码;

可以获取到系统的运行状态,方便进行监控。

缺点:

不能精细地控制需要记录的操作和不需要记录的操作;

不能记录用户的操作内容;

只能获取到系统的运行状态,不能记录其他自定义的操作信息。

个人推荐:

这可能不是最适合你的方式,因为不同的场景和需求可能会需要不同的方式。我认为比较推荐的两种方式和它们的优点。

1、利用 AOP 切面拦截请求。这种方式具有高度的灵活性,可以拦截任意的请求,并且可以通过切面的方式进行统一的处理,减少了代码的冗余。此外,AOP 还支持对请求的参数、返回值等进行拦截和处理。缺点是配置比较繁琐,需要使用 AOP 技术,对开发人员的技能要求比较高。

2、利用注解方式记录操作日志。这种方式比较简单,只需要在需要记录操作日志的方法上添加注解即可,对代码的侵入性比较小。此外,可以根据注解的不同实现不同的日志记录策略。缺点是需要开发人员手动添加注解,如果有大量的方法需要记录操作日志,会比较繁琐。

总结不易,如果对您有帮助,希望您点赞、收藏、评论。有问题可以留言看到会回复大家,谢谢。

继续阅读