🎉🎉《Spring Boot实战案例合集》目前已更新195个案例,我们将持续不断的更新。文末有电子书目录。
💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。
💌💌如何获取
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.4.2
1. 简介
在分布式系统及复杂业务场景下,日志作为系统运行的关键记录,其有效管理与分析至关重要。请求跟踪能监控流转路径、定位故障,而MDC作为线程绑定的诊断工具,可关联链路数据辅助排查。但日志量庞大时,精准过滤与高效处理成为难题。Logback的TurboFilter能在日志处理早期进行精细控制,满足基于动态条件过滤等需求,为日志管理提供了有力手段。
本篇文章我们将深入探讨日志处理领域中请求跟踪与日志过滤两大关键技术。
请求跟踪可全程监控请求流转路径,精准定位性能瓶颈与故障点。结合MDC,能在日志里高效关联链路数据。
MDC(Mapped Diagnostic Context)是线程绑定的诊断上下文工具,可存储键值对信息。在请求跟踪中,它能在日志里自动关联请求链路数据,助力快速定位问题,提升系统可观测性与故障排查效率。
接下来,我们借助MDC在日志中植入唯一ID,以此构建完整的请求链路标识,实现精准高效的请求跟踪。
public class TraceXFilter implements Filter {private static final Logger logger = LoggerFactory.getLogger(TraceXFilter.class) ;public static final String TRACE_KEY = "traceXId" ;public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request ;// 通过请求header获取自定义的traceId,没有则系统生成String traceId = req.getHeader("x-trace") ;if (!StringUtils.hasLength(traceId)) {traceId = UUID.randomUUID().toString().replace("-", "").toUpperCase() ;}// 将当前请求的traceId存入到MDC中MDC.put(TRACE_KEY, traceId) ;logger.info("请求: {}", req.getServletPath()) ;try {chain.doFilter(request, response) ;} finally {MDC.remove(TRACE_KEY) ;}}}
<configuration scan="true"><contextName>trace-mdc</contextName><!-- 注意这里的traceXId就是我们往MDC中存入的key了,通过%X{traceXId}获取唯一标识 --><property name="TRACEX_PATTERN"value="%green(%d{HH:mm:ss}) traceId:【%red(%X{traceXId})】 %highlight(%-5level) [%yellow(%thread)] %logger Line:%-3L - %msg%n" /><appender name="TRACEX" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>${TRACEX_PATTERN}</pattern><charset>UTF-8</charset></encoder></appender><springProfile name="dev | default"><root level="INFO"><appender-ref ref="TRACEX" /></root></springProfile></configuration>
private final static Logger logger = LoggerFactory.getLogger(ApiController.class) ;public ResponseEntity<?> query() {logger.info("query start...") ;return ResponseEntity.ok("api query...") ;}
在当前请求的链路里,日志具备唯一标识后,我们能够更加便捷、高效地排查问题。
TurboFilter在日志处理流程的非常早期阶段就被调用——甚至在对日志级别进行检查之前。这意味着,一旦日志框架看到日志语句,你就可以立即决定是否让该日志语句继续处理。
通常在以下情况下,我们是非常需要TurboFilter:
你想基于动态条件(如会话中的用户ID)来过滤日志
在高性能应用中,你需要超快速地决定是否记录某些内容
你想避免日志污染,在日志消息甚至还未输出到控制台或日志文件之前就将其丢弃
你需要在日志生命周期的早期阶段使用映射诊断上下文(Mapped Diagnostic Context,MDC)或标记(Markers)
接下来,我们通过TurboFilter实现日志过滤功能。
我们只需要在logback-spring.xml中加入如下配置:
<configuration><turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"><MDCKey>userId</MDCKey><Value>admin</Value><OnMatch>ACCEPT</OnMatch><OnMismatch>DENY</OnMismatch></turboFilter></configuration>
有了该配置它会检查MDC键userId是否等于“admin”。如果匹配,则接受并处理日志消息。如果不匹配,则丢弃日志消息——避免日志文件杂乱!
修改TraceXFilter过滤器如下:
public class TraceXFilter implements Filter {public void doFilter(...) {MDC.put("userId", request.getParameter("name")) ;}}
接下来,我们继续访问上面的/api/query?name=admin接口,控制台输出:
而当name是其它取值,控制台将不输出任何内容。
在该日志输出情况看到,这时候不管什么级别的日志都输出了,这是由于我们配置的OnMatch=ACCEPT的原因,这将完全绕过 <root level="INFO"> 的级别控制。我们将其修改为NEUTRAL,这样就继续后续过滤器或root级别判断。输出如下:
public class PackTurboFilter extends TurboFilter {private static final String SENSITIVE_LOG = "com.pack.sensitive" ;public FilterReply decide(Marker marker, ch.qos.logback.classic.Logger logger, Level level, String format,Object[] params, Throwable t) {if (level.isGreaterOrEqual(Level.WARN)) {return FilterReply.ACCEPT;}if (SENSITIVE_LOG.equals(logger.getName())) {return FilterReply.DENY;}String userId = getContext().getCopyOfPropertyMap().get("userId") ;if ("admin".equals(userId)) {return FilterReply.ACCEPT ;}return FilterReply.NEUTRAL;}}
基于日志级别的过滤
基于Logger名称的过滤
基于MDC值的过滤
<configuration><turboFilter class="com.pack.mdc.filter.PackTurboFilter"/></configuration>
总结:TurboFilter 的优势在于其早期过滤能力,但不当使用可能会:
引入不必要的同步开销
增加内存使用(如果存储状态)
意外过滤掉重要日志
强大!Spring非常实用的内置类专门处理Rest API异常
性能优化!Spring Boot 优化JPA插入操作,性能提升N倍,尤其批量操作
Spring Boot 基于注解实现字段级完整性校验,支持JPA、Mybatis
别再错过!Spring Boot 12个高级开发技巧,助你成为大神
告别下载风险!Spring Boot 打造安全的文件下载功能
告别内存溢出!Spring StreamingResponseBody 三大实战案例,性能提升100%
API接口优化!基于Spring Boot 实现Deflate压缩技术


