Search code examples
spring-webfluxproject-reactorspring-webclient

Accessing Context inside an ExchangeFilterFunction


For some reason a context inside the doAfterSuccessOrError method is not available (populated) from the upstream. I've tried to access it using Mono.subscriberContext() (see the snipped). I would expect to have it present but for some reason is not. Am I doing something wrong?

public class LoggingRequestExchangeFunction implements ExchangeFilterFunction {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        long start = System.currentTimeMillis();

        return next.exchange(request).doAfterSuccessOrError((res, ex) -> {
            Mono.subscriberContext().map((ctx -> {
                log.info("doAfterSuccessOrError Context {}",ctx);
                // log req/res ...
                return ctx;
            })).subscribe();

        }).subscriberContext( ctx -> {
            log.info("SubscriberContext: {}" , ctx);
            return ctx;
        });
    }
}

Here is a log output

23:16:59.426  INFO [reactor-http-epoll-2] .p.c.LoggingRequestExchangeFunction [] SubscriberContext: Context1{nexmo-tracing-context=TracingContext{{traceId=f04961da-933a-4d1d-85d5-3bea2c47432f, clientIp=N/A}}}
23:16:59.589  INFO [reactor-http-epoll-2] .p.c.LoggingRequestExchangeFunction [] doAfterSuccessOrError Context Context0{}

Solution

  • The reason is that you create a new Mono inside doAfterSuccessOrError which is independent from the original reactor chain since you subscribe to it separately.

    If you just want to log something inside, your alternative is to use doOnEach operator which beside the signal type gives you access to the context as well.

    Mono.just("hello")
        .doOnEach((signal) ->
                {
                    if (signal.isOnError() || signal.isOnComplete())
                    {
                        Context ctx = signal.getContext();
    
                        log.info("doAfterSuccessOrError Context {}",ctx);
                        // log req/res ...
                    }
                })
        .subscriberContext( ctx -> {
                log.info("SubscriberContext: {}" , ctx);
                return ctx;
          })
        .subscribe();