MDC vs Reactor's "Context"
cristigirbovan opened this issue · 1 comments
While setting an MDC value at the beginning of a request might seem straightforward, in a reactive context, I think it's essential to ensure that the MDC values are propagated across any thread switches that might occur during the request's lifecycle. Without proper propagation, you might end up with missing or incorrect log context in parts of your request processing. When you set a value in the MDC at the beginning of a reactive request, you're indeed setting it for the thread that initially handles the request. However, due to the nature of reactive programming, the processing of that request might get handed off to another thread at some point, especially if there are asynchronous operations involved, an I/O call, a delay, etc. I think even if the code/method itself doesn't contain asynchronous operations, external factors might cause thread switches. For instance, if the system is under heavy load, or if there are other tasks queued up in the event loop, the framework might decide to switch threads to optimize throughput. The MDC works by storing data in a ThreadLocal variable. This means the data is specific to the current thread. If the reactive processing switches to another thread, the MDC values set in the initial thread won't be available in the new thread, leading to potential issues or missing log context. Am I missing something?
So to optimize this I think we should use something like this:
return chain.filter(exchangeDecorator).contextWrite(ctx -> ctx.put("key", value))
...........................
But in the logback.xml we can add values to the pattern only if they are already in MDC, using %X{key} where key is the name of the value you've set in the MDC:
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{key} - %msg%n</pattern>
So, I think if we want to use the %X{key} pattern in Logback to log a value, that value must be present in the MDC. Even if the value is in the Reactor Context, you'll need to transfer it to the MDC before logging. But how to do that, what is the best way to implement this? Honestly I don't know, maybe:
-
use a utility yo set the values to MDC
public static void setContextToMDC(Context ctx, String key, String value) {
MDC.clear();
ctx.stream().forEach(entry -> MDC.put(key, value));
} -
use reactor's doOnEach"
return chain.filter(exchangeDecorator)
.contextWrite(ctx -> ctx.put("key", "value"))
.doOnEach(signal -> {
if (signal.isOnNext() || signal.isOnError()) {
setContextToMDC(signal.getContext(),"key","value");
}
});
Or maybe I'm wrong?!?
Thanks a lot
Maybe you could propose it via a PR?