Contextual logging (corelation id in headers ) with webflux

Viewed 1762

I need some params from the headers inside my coroutine in my controller, and log them as a corelation id for my requests.

Is it possible to use webflux / kotlin coroutines in controller AND to do contextual logging with the params in the header ?

I know Webflux can use WebFilter to intercept headers and log them or modify them, but can it be sent to the coroutine it will trigger ?

@RestController
class ItemController(private val itemRepository: ItemRepository) {

    @GetMapping("/")
    suspend fun findAllItems(): List<Item> =
        // do stuff
        logger.log("Corelation id is : " + myCorelationIdHeaderParam) // that's the param i need
        return itemService.findAll()
}
2 Answers

Any context you set in the webfilter can be accessed by using subscriberContext down the line in your controllers/services.

Below is an example using Java. You can use similar logic in your Kotlin code:

Your filter: (Here you are setting the header value "someHeaderval" in your "myContext" )

public class MyFilter implements WebFilter {


  @Override
  public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    String someHeaderval = request.getHeaders().get("someHeader").get(0);
    return chain.filter(exchange).subscriberContext(context -> {
                  return context.put("myContext",someHeaderval);
                });;
  }

}

Now you can use this context anywhere:

@GetMapping(value = "/myGetApi")
  public Mono<String> sampleGet() {
    return Mono.subscriberContext()
        .flatMap(context -> {
          String myHeaderVal = (String)context.get("myContext");
          //do logging with this header value
          return someService.doSomething(myHeaderVal);
        });
  }

It turns out you can access the ReactorContext from the CoroutineContext with

coroutineContext[ReactorContext]

Here is my code :

@Component
class MyWebFilter : WebFilter {
    val headerKey = "correlation-token-key"
    val contextKey = "correlationId"
    override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
        val headers: HttpHeaders = exchange.request.headers
        return chain.filter(exchange)
            .subscriberContext(Context.of(contextKey, headers[headerKey] ?: "unidentified"))

    }
}

and the controller part (most important for Kotlin users) :

@RestController
class ItemController(private val itemRepository: ItemRepository) {
    @GetMapping("/")
    suspend fun findAllItems(): List<Item> =
        // do stuff
        logger.log("Correlation id of request is : " + coroutineContext[ReactorContext]?.context?.get<List<String>>("correlationId")?.firstOrNull()) 
        return itemService.findAll()
}
Related