I referenced with the blog post Contextual Logging with Reactor Context and MDC but I don't know how to access reactor context in 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);
}
}
logOnNext
wrapper. Use Context PropagationI started working with Reactor back in 2018, and thus far, there hasn't been a truly good alternative to the wrapper method inside doOnNext
where you manually copy your trace fields from Reactor's Context into MDC, making your own ad-hoc bridge between reactive and imperative worlds, and voilà, your logs now could make sense. But things changed, and finally, a new solution is here – Context propagation. Let's take a look at it.
Imagine that you have your Spring service, and you have a set of fields that define your diagnostic context, the fields you use for tracing the service's activities. Let's assume that you store that set as the following property: management.tracing.baggage.correlation.fields: trace, session
.
Now, to have these fields automatically populated into the MDC of the thread that executes the call of your reactive chain, sourcing the keys-values from the reactive context, you just need to add context-propagation library to your project and after do the following service-wide configuration:
/**
* 1. Will register ThreadLocalAccessors into ContextRegistry for fields listed in application.yml as property value
* <b>management.tracing.baggage.correlation.fields</b>
* 2. Enables Automatic Context Propagation for all reactive methods
*
* @see <a href="https://github.com/micrometer-metrics/context-propagation">context-propagation</a>
*/
@Configuration
@ConditionalOnClass({ContextRegistry.class, ContextSnapshotFactory.class})
@ConditionalOnProperty(value = "management.tracing.baggage.correlation.fields", matchIfMissing = true)
public class MdcContextPropagationConfiguration {
public MdcContextPropagationConfiguration(@Value("${management.tracing.baggage.correlation.fields}")
List<String> fields) {
if (!isEmpty(fields)) {
fields.forEach(claim -> ContextRegistry.getInstance()
.registerThreadLocalAccessor(claim,
() -> MDC.get(claim),
value -> MDC.put(claim, value),
() -> MDC.remove(claim)));
return;
}
Hooks.enableAutomaticContextPropagation();
}
}
The trick here is to use Hooks.enableAutomaticContextPropagation()
. As soon as we register a set of ThreadLocalsAccessors for propagation that maps out the tracing fields, the hook will ensure the passing on of the values under the keys of the registered fields from the reactive context into MDC on each call of your chain. These fields within MDC subsequently could be referenced in slf4j appenders in any preference.
That's it.