使用 Log4j2 在反应式 Spring Boot 中根据请求标头动态设置每个请求的 DEBUG 或 INFO 日志记录级别

问题描述 投票:0回答:1

我正在开发一个 Reactive Spring Boot 应用程序,它使用 Log4j2 作为日志记录框架。默认情况下,我的应用程序在

INFO
级别进行日志记录,并且我在
application.yml

中设置日志记录级别

我希望能够在

DEBUG
级别记录仅针对一个特定请求,而无需全局更改日志记录级别或影响其他并发请求。例如,如果我为请求传递自定义标头(例如,
X-Debug-Logging: true
),我希望该请求在
DEBUG
级别记录,但其他并行请求应继续在默认
INFO
级别记录。

我尝试过的: 我已经实现了一个 WebFilter 来检查 X-Debug-Logging 标头并调整日志记录级别,但我担心由于 Reactive Spring 异步处理请求,更改日志级别可能会影响同时处理的其他请求。

问题: 如何使用

Log4j2
Reactive Spring 应用程序中以 DEBUG 级别动态记录单个请求,同时确保其他并发请求继续以默认
INFO
级别记录?

用例:

  • 如果请求 A 有
    X-Debug-Logging: true
    ,则应记录在
    DEBUG
  • 如果请求 B 没有此标头,则应记录在
    INFO
    (默认)。
  • 请求应保持隔离,并且日志级别更改应 只影响特定请求,不影响其他请求。
spring-boot logging log4j2 slf4j
1个回答
0
投票

您正在寻找的是

DynamicThresholdFilter
ContextMapFilter
,它们允许您根据当前上下文数据过滤消息。

当过滤器用作全局过滤器时(即

<Configuration
> 的直接子级):

  • 结果
    ACCEPT
    将无条件接受该消息,
  • NEUTRAL
    的结果还将检查配置的记录器级别。

(详情请参阅记录器阶段)。

所以你需要做的就是:

  1. 在每个请求中,向Log4j API线程上下文添加一个值(例如
    sampled
    )并将其传播到所有线程,以便处理该请求。
  2. 配置 Log4j 过滤器以返回
    ACCEPT
    ,如果
    sampled
    为 true,则返回
    NEUTRAL

设置和传播线程上下文

正如您所注意到的,传播价值可能是最困难的任务。为此,我建议您阅读Dariusz 关于上下文传播的博客系列。在这种特殊情况下,您需要:

  • io.micrometer:context-propagation
    添加到您的应用程序中:
    <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>context-propagation</artifactId>
    </dependency>
    
  • 在应用程序的
    main
    方法中启用传播:
    Hooks.enableAutomaticContextPropagation();
    
  • 在您的
    ThreadLocalAccessor
    方法中为 Log4j
    ThreadContext
    添加
    main
    。没有直接访问器,但 SLF4J
    MDC
    桥接到
    ThreadContext
    ContextRegistry.getInstance().registerThreadLocalAccessor(new Slf4jThreadLocalAccessor());
    
  • 添加一个
    WebFilter
    ,用于从线程上下文映射中设置和删除
    sampled
    键:
    @Component
    public class TracingFilter implements WebFilter {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            boolean sampled =
                    Boolean.parseBoolean(exchange.getRequest().getHeaders().getFirst("X-Debug-Logging"));
            return chain.filter(exchange).contextWrite(c -> {
                // Get a copy of the MDC map from the Reactor context
                Map<String, Object> mdc =
                        new HashMap<>(c.getOrDefault(Slf4jThreadLocalAccessor.KEY, Collections.emptyMap()));
                // Add or remove an entry
                mdc.put("sampled", sampled ? "true" : null);
                // Return a modified context
                return c.put(Slf4jThreadLocalAccessor.KEY, Collections.unmodifiableMap(mdc));
            });
        }
    }
    

配置 Log4j 过滤器

您可以使用

DynamicThresholdFilter
,其中:

  • 如果
    DEBUG
    sampled
    ,则对照 
    true
  • 检查消息
  • 如果
    defaultThreshold
    有任何其他非
    sampled
    值,则对照
    null
    检查消息,
  • 否则返回
    onMismatch
<Configuration xmlns="https://logging.apache.org/xml/ns"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="
                   https://logging.apache.org/xml/ns
                   https://logging.apache.org/xml/ns/log4j-config-2.xsd">
  <DynamicThresholdFilter key="sampled"
                          onMatch="ACCEPT"
                          onMismatch="NEUTRAL">
    <KeyValuePair key="true" value="DEBUG"/>
  </DynamicThresholdFilter>
  <!-- Your `Appenders` and `Loggers` go here: -->
</Configuration>
© www.soinside.com 2019 - 2024. All rights reserved.