question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RequestContextHolder for Reactive Web [SPR-15680]

See original GitHub issue

Doron Gold opened SPR-15680 and commented

It is necessary to have a way to associate attributes with the current request and be able to fetch these attributes during the request lifetime.

The ability to set and get such attributes is essential for use-cases such as:

  • Having a unique trace-id per request, which can be included in all log messages.
  • Identifying the currently logged in user (the user associated with the request).
  • In a micro-service architecture: receiving contextual data via request headers, then when calling other micro-services, pass the same contextual data that came with the original request.

In webmvc this is possible by calling a static methods on org.springframework.web.context.request.RequestContextHolder. A developer could implement HandlerInterceptor which intercepts all incoming requests and sets contextual attributes on RequestContextHolder. These attributes are then accessible from anywhere in the code (via a static method) during the request lifetime.

Since in Reactive Web a request is not bound to a single processing thread, a simple use of ThreadLocal (what RequestContextHolder does) is not enough. The Spring framework should offer a more sophisticated solution.

A simple but extremely intrusive workaround would be to pass in ServerWebExchange to all methods in all components - starting from controller endpoints down to all services. But even if this is done, there is no good way to have a logger take attributes form the request (for the purpose of including trace/correlation ID) without implementing a wrapper around the logger class that receives an extra parameter.


Reference URL: https://stackoverflow.com/questions/43975761/how-to-get-the-context-of-the-current-request-in-spring-webflux

Issue Links:

  • #21746 Create a WebFilter for ServerWebExchange Reactor Context
  • #20108 Upgrade to Reactor 3.1 RC1 (including Reactive Streams 1.0.1)

19 votes, 34 watchers

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:40 (1 by maintainers)

github_iconTop GitHub Comments

3reactions
carlosbarragancommented, Apr 5, 2019

@scottjohnson @archie-swif please be aware that the context should remain as small as possible. Instead of adding a new key-value entry, you should simply add a new key that references a map. That is what is recommended in the JavaDocs of the Context.

So you need to do something like this:

.subscriberContext((context) -> {
                   Map<String, String> map = new HashMap();
                    map.put("foo","bar");
		    context = context.put("myContextKey",map)                   
                    return context;
                });
1reaction
alejandroarevalocommented, Apr 13, 2021

@torstenwerner @ivansenic @carlosbarragan @archie-swif We have followed https://github.com/archie-swif/webflux-mdc/tree/master/src/main/java/com/example/webfluxmdc and we are logging a context called correlationId which is being sent in the header of a particular api request. But also for other simultaneous api calls with no correlationId header, the same correlationId gets logged. For ex: When we hit GET /offers endpoint with header correlationId 'abc’ , if there are simultaneous api calls for GET /metrics endpoint without any header correlationId, we get the same correlationId ‘abc’ for /metrics calls also. How can we get rid of this issue?

Hi @ankitp-kr ,

I’m also trying to use MdcContextLifter proposed by @archie-swif , since unfortunately the approach recommended in https://projectreactor.io/docs/core/release/reference/#faq.mdc is not sufficient for my use case ( I need to produce some logging that come from internal libraries invoked within each operator, and I would like to have this libraries log the MDC variables as well)

So I’ve opted for a similar approach as the one @archie-swif used, but using a closeable MDC for adding my correlationId, could you maybe try this to see if you get rid of the problem in where simultaneous invocations, one with correlation ID and one without it, would print same value?

class MdcContextLifter<T> implements CoreSubscriber<T> {

    CoreSubscriber<T> coreSubscriber;

    public MdcContextLifter(CoreSubscriber<T> coreSubscriber) {
        this.coreSubscriber = coreSubscriber;
    }

    @Override
    public void onSubscribe(Subscription subscription) {
        coreSubscriber.onSubscribe(subscription);
    }

    @Override
    public void onNext(T obj) {
        injectMdc(() -> coreSubscriber.onNext(obj));
    }

    @Override
    public void onError(Throwable t) {
        injectMdc(() -> coreSubscriber.onError(t));
    }

    @Override
    public void onComplete() {
        injectMdc(() -> coreSubscriber.onComplete());
    }

    @Override
    public Context currentContext() {
        return coreSubscriber.currentContext();
    }

    /**
     * Adding correlationId in MDC as closeable statement.
     * @param task
     */
    private void injectMdc(Runnable task) {
        final Object mdcVal = coreSubscriber.currentContext().getOrDefault("correlationId", null);
        if(mdcVal != null ) {
            try(MDC.MDCCloseable ignored = MDC.putCloseable("correlationId", mdcVal.toString())) {
                task.run();
            }
        } else {
            task.run();
        }
    }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Spring RequestContextHolder and WebTestClient
RequestContextHolder primary works using java.lang.ThreadLocal . This clearly doesn't fit with Reactor architecture. Having said that, We can ...
Read more >
RequestContextHolder (Spring Framework 6.0.2 API)
Holder class to expose the web request in the form of a thread-bound RequestAttributes object. The request will be inherited by any child...
Read more >
Retaining Context From In Reactive Streams - ADocLib
In the Spring boot web middle we can RequestContextHolder get easily request. REACTIVE public class ReactiveRequestContextFilter implements WebFilter. Besides ...
Read more >
Get Request object anywhere in Spring WebFlux
The class RequestContextHolder provides static methods, which means you can call it from anywhere. And it uses ThreadLocal to hold the Request ...
Read more >
Example usage for org.springframework.web.context.request ...
RequestContextHolder ; setRequestAttributes ... example usage for org.springframework.web.context.request RequestContextHolder ... ReactiveInvokerTest.java
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found