1 简介
在Web应用程序的世界中,有效的请求监控和可追踪性对于维护系统完整性和诊断问题至关重要。Spring Boot是一种流行的Java框架,用于构建应用程序,并提供强大的日志记录功能。为了进一步丰富这一功能,可以通过为每个传入请求添加唯一标识符来增强日志记录能力。这种简洁而强大的实践使开发人员能够跟踪和分析单个请求的流程,简化调试,并增强整个系统的可靠性。
本文我们强调在Spring Boot中为日志添加唯一标识符的重要性,介绍其实现方式以及其为应用程序开发和维护带来的众多优势好处。
2 问题描述
如今,每个服务通常都有多个服务器实例。有两种方法可以查看日志,一种是逐个访问各个服务器实例以查看日志,另一种是收集所有服务器的日志到一个集中地点进行查看。通常情况下,人们会选择第二种方法,使用像GCP中的Log Explorer这样的工具来搜索和分析日志。然而,由于来自多个服务器的大量日志,追踪这些日志变得困难。
此外,即使对于只有一个服务的单个服务器,由于并发请求和线程,日志会被打印出很多条。其中一个主要问题是日志打印的交错性,很难跟踪打印出的日志。
3 解决方案
为了解决上述问题,需要为每个日志条目关联一种标识符。我们可以生成一个唯一的ID,然后将该ID分配给特定请求的所有日志。因此,为特定请求生成的所有日志都具有唯一的ID。借助这个ID,可以便捷地跟踪日志。我们可以在日志中搜索此ID并获取特定请求的所有日志。在Spring Boot中实现这一点,可以使用AOP。
面向方面的编程(AOP)是一种编程范式,允许开发人员将横切关注点(如日志记录、安全性和事务管理)与核心业务逻辑分开模块化,促进代码的可重用性和可维护性。它通过引入“切面”在代码库的不同部分应用,而无需修改原始代码。
首先,需要创建一个请求过滤器,如下所示:
/**
* A filter that adds a key to the Mapped Diagnostic Context (MDC) to each request so you can print a unique id in the log messages of each request
**/
@EqualsAndHashCode(callSuper = false)
@Component
@Slf4j
public class Slf4jMDCFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) {
try {
MDC.put(MDC_UUID_TOKEN_KEY, UUID.randomUUID().toString());
chain.doFilter(request, response);
} catch (Exception ex) {
log.error(ERROR_FORMAT, "Exception occurred in filter while setting UUID for logs", ex);
} finally {
MDC.remove(MDC_UUID_TOKEN_KEY);
}
}
@Override
protected boolean isAsyncDispatch(final HttpServletRequest request) {
return false;
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
}
上述代码对每个API请求运行一次,并将相同的唯一ID添加到为特定请求生成的所有日志中。接下来,需要将上述AOP过滤器注册为一个bean,如下所示:
@Configuration
@RequiredArgsConstructor
public class BeanConfig {
private final Slf4jMDCFilter slf4jMDCFilter;
@Bean
public FilterRegistrationBean<Slf4jMDCFilter> servletRegistrationBean() {
final FilterRegistrationBean<Slf4jMDCFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(slf4jMDCFilter);
filterRegistrationBean.setOrder(2);
return filterRegistrationBean;
}
}
然后,需要在日志配置文件中添加一个appender,如下所示:
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>
%black(%d{ISO8601}) %X{Slf4jMDCFilter.UUID} %highlight(%-5level) [%blue(%t)] %yellow(%C{1}.): %msg%n%throwable
</Pattern>
</layout>
</appender>
4 结语
在Spring Boot中为每个请求的日志实现唯一标识符可以通过关联应用程序的不同组件中的日志,特别是在并发情况下,提供更好的可追踪性和调试能力。这个唯一标识符有助于跟踪请求的流程,识别潜在问题,并提高整个系统的可观察性。