天天看點

讓你一篇文章搞明白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、利用注解方式記錄記錄檔。這種方式比較簡單,隻需要在需要記錄記錄檔的方法上添加注解即可,對代碼的侵入性比較小。此外,可以根據注解的不同實作不同的日志記錄政策。缺點是需要開發人員手動添加注解,如果有大量的方法需要記錄記錄檔,會比較繁瑣。

總結不易,如果對您有幫助,希望您點贊、收藏、評論。有問題可以留言看到會回複大家,謝謝。

繼續閱讀