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 請求相關資訊,包括記錄檔。
下面逐一介紹下具體實作流程
一、“利用 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 子產品記錄記錄檔的具體實作流程和代碼。
下面總結下每種方式的優缺點
1、利用 AOP 切面攔截請求
優點:
可以精細地控制需要記錄的操作和不需要記錄的操作;
可以統一處理異常,避免記錄記錄檔時出現異常而中斷;
可以通過注解方式友善地對不同的操作類型進行區分。
缺點:
會對系統性能造成一定的影響;
需要熟悉 AOP 的使用方法。
2、利用 Filter 過濾器攔截請求
優點:
可以攔截所有請求,無需配置;
可以統一處理異常,避免記錄記錄檔時出現異常而中斷;
可以通過注解方式友善地對不同的操作類型進行區分。
缺點:
不能精細地控制需要記錄的操作和不需要記錄的操作;
會對系統性能造成一定的影響;
需要熟悉 Filter 的使用方法。
3、利用注解方式記錄記錄檔
優點:
可以精細地控制需要記錄的操作和不需要記錄的操作;
可以通過注解方式友善地對不同的操作類型進行區分。
缺點:
需要在每個需要記錄記錄檔的方法上添加注解,工作量較大;
如果有很多需要記錄記錄檔的方法,容易造成代碼備援。
4、利用攔截器攔截請求
優點:
可以攔截所有請求,無需配置;
可以統一處理異常,避免記錄記錄檔時出現異常而中斷;
可以通過注解方式友善地對不同的操作類型進行區分。
缺點:
不能精細地控制需要記錄的操作和不需要記錄的操作;
會對系統性能造成一定的影響;
需要熟悉攔截器的使用方法。
5、利用 Spring Boot 的 Actuator 子產品記錄記錄檔
優點:
可以快速地內建到 Spring Boot 項目中,無需編寫額外的代碼;
可以擷取到系統的運作狀态,友善進行監控。
缺點:
不能精細地控制需要記錄的操作和不需要記錄的操作;
不能記錄使用者的操作内容;
隻能擷取到系統的運作狀态,不能記錄其他自定義的操作資訊。
個人推薦:
這可能不是最适合你的方式,因為不同的場景和需求可能會需要不同的方式。我認為比較推薦的兩種方式和它們的優點。
1、利用 AOP 切面攔截請求。這種方式具有高度的靈活性,可以攔截任意的請求,并且可以通過切面的方式進行統一的處理,減少了代碼的備援。此外,AOP 還支援對請求的參數、傳回值等進行攔截和處理。缺點是配置比較繁瑣,需要使用 AOP 技術,對開發人員的技能要求比較高。
2、利用注解方式記錄記錄檔。這種方式比較簡單,隻需要在需要記錄記錄檔的方法上添加注解即可,對代碼的侵入性比較小。此外,可以根據注解的不同實作不同的日志記錄政策。缺點是需要開發人員手動添加注解,如果有大量的方法需要記錄記錄檔,會比較繁瑣。
總結不易,如果對您有幫助,希望您點贊、收藏、評論。有問題可以留言看到會回複大家,謝謝。