如何在spring-webflux WebFilter中正确使用slf4j MDC

时间:2018-08-08 03:14:31

标签: java spring-webflux project-reactor

我引用了博客文章Contextual Logging with Reactor Context and MDC,但我不知道如何在WebFilter中访问反应堆上下文。

@Component
public class RequestIdFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        List<String> myHeader =  exchange.getRequest().getHeaders().get("X-My-Header");

        if (myHeader != null && !myHeader.isEmpty()) {
            MDC.put("myHeader", myHeader.get(0));
        }

        return chain.filter(exchange);
    }
}

2 个答案:

答案 0 :(得分:6)

您可以执行类似于以下的操作,可以为您喜欢的任何类设置TypeError: Cannot read property 'root' of undefined at new dt (/Users/eduardo/sites/tr2/dist/server.js:2176:2142) at /Users/eduardo/sites/tr2/dist/server.js:409:1427 at Wi (/Users/eduardo/sites/tr2/dist/server.js:409:1644) at /Users/eduardo/sites/tr2/dist/server.js:424:8973 at new e (/Users/eduardo/sites/tr2/dist/server.js:424:8983) at Object.go [as createNgModuleRef] (/Users/eduardo/sites/tr2/dist/server.js:424:8621) at n.create (/Users/eduardo/sites/tr2/dist/server.js:482:635) at /Users/eduardo/sites/tr2/dist/server.js:229:1479 at e.invoke (/Users/eduardo/sites/tr2/dist/server.js:2907:7001) at Object.onInvoke (/Users/eduardo/sites/tr2/dist/server.js:215:2040) ,在本示例中,我仅使用标头-但是自定义类就可以了。 如果在此处进行设置,则任何带有处理程序等的日志记录也将有权访问Angular CLI: 6.1.2 Node: 9.11.1 OS: darwin x64 Angular: 6.1.1 ... animations, common, compiler, compiler-cli, core, forms ... http, language-service, platform-browser ... platform-browser-dynamic, platform-server, router Package Version @angular-devkit/architect 0.7.2 @angular-devkit/build-angular 0.7.2 @angular-devkit/build-optimizer 0.7.2 @angular-devkit/build-webpack 0.7.2 @angular-devkit/core 0.7.2 @angular-devkit/schematics 0.7.2 @angular/cdk 6.2.1 @angular/cli 6.1.2 @angular/material 6.2.1 @ngtools/webpack 6.1.2 @schematics/angular 0.7.2 @schematics/update 0.7.2 rxjs 6.2.2 typescript 2.9.2 webpack 4.9.2
下面的context设置MDC并在之后清除它。显然,可以用任何您喜欢的东西代替它。

context

答案 1 :(得分:3)

这是一种基于最新方法的解决方案,截至 2021 年 5 月,取自 official documentation

Measure-Command

假设您的 import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import reactor.core.publisher.Mono; import reactor.core.publisher.Signal; import reactor.util.context.Context; @Slf4j @Configuration public class RequestIdFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String requestId = getRequestId(request.getHeaders()); return chain .filter(exchange) .doOnEach(logOnEach(r -> log.info("{} {}", request.getMethod(), request.getURI()))) .contextWrite(Context.of("CONTEXT_KEY", requestId)); } private String getRequestId(HttpHeaders headers) { List<String> requestIdHeaders = headers.get("X-Request-ID"); return requestIdHeaders == null || requestIdHeaders.isEmpty() ? UUID.randomUUID().toString() : requestIdHeaders.get(0); } public static <T> Consumer<Signal<T>> logOnEach(Consumer<T> logStatement) { return signal -> { String contextValue = signal.getContextView().get("CONTEXT_KEY"); try (MDC.MDCCloseable cMdc = MDC.putCloseable("MDC_KEY", contextValue)) { logStatement.accept(signal.get()); } }; } public static <T> Consumer<Signal<T>> logOnNext(Consumer<T> logStatement) { return signal -> { if (!signal.isOnNext()) return; String contextValue = signal.getContextView().get("CONTEXT_KEY"); try (MDC.MDCCloseable cMdc = MDC.putCloseable("MDC_KEY", contextValue)) { logStatement.accept(signal.get()); } }; } } 中有以下行:

application.properties

那么每次调用端点时,您的服务器日志都会包含这样的日志:

logging.pattern.level=[%X{MDC_KEY}] %5p

每次您想在响应式上下文中手动记录某些内容时,您都需要将以下内容添加到响应式链中:

2021-05-06 17:07:41.852 [60b38305-7005-4a05-bac7-ab2636e74d94]  INFO 20158 --- [or-http-epoll-6] my.package.RequestIdFilter    : GET http://localhost:12345/my-endpoint/444444/

如果您希望将 .doOnEach(logOnNext(r -> log.info("Something"))) 传播到其他服务以进行分布式跟踪,您需要从响应式上下文(而不是从 MDC)读取它并使用以下内容包装您的 X-Request-ID 代码:< /p>

WebClient