7

Basically, I want to log a request/response informations in one log containing bodies/headers with a Spring WebClient.

With Spring RestTemplate we can do it with a ClientHttpRequestInterceptor. I find about ExchangeFilterFunction for Spring WebClient but haven't managed to do something similar in a clean way. We can use this filter and log the request AND THEN the response but I need both on the same log trace.

Moreover, I haven't managed to get the response body with ExchangeFilterFunction.ofResponseProcessor method.

I expect a log like this (current implementation working with a ClientHttpRequestInterceptor) with all the informations I need:

{
    "@timestamp": "2019-05-14T07:11:29.089+00:00",
    "@version": "1",
    "message": "GET https://awebservice.com/api",
    "logger_name": "com.sample.config.resttemplate.LoggingRequestInterceptor",
    "thread_name": "http-nio-8080-exec-5",
    "level": "TRACE",
    "level_value": 5000,
    "traceId": "e65634ee6a7c92a7",
    "spanId": "7a4d2282dbaf7cd5",
    "spanExportable": "false",
    "X-Span-Export": "false",
    "X-B3-SpanId": "7a4d2282dbaf7cd5",
    "X-B3-ParentSpanId": "e65634ee6a7c92a7",
    "X-B3-TraceId": "e65634ee6a7c92a7",
    "parentId": "e65634ee6a7c92a7",
    "method": "GET",
    "uri": "https://awebservice.com/api",
    "body": "[Empty]",
    "elapsed_time": 959,
    "status_code": 200,
    "status_text": "OK",
    "content_type": "text/html",
    "response_body": "{"message": "Hello World!"}"
}

Does anyone manage to do something like this with Spring WebClient ? Or how would one proceed to track request/reponses issue with a Spring WebClient ?

nsphung
  • 173
  • 1
  • 1
  • 9

2 Answers2

3

You can use filter(), something like this:

this.webClient = WebClient.builder().baseUrl("your_url")
            .filter(logRequest())
            .filter(logResponse())
            .build();

private ExchangeFilterFunction logRequest() {
    return (clientRequest, next) -> {
        log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
        clientRequest.headers()
                .forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
        return next.exchange(clientRequest);
    };
}

private ExchangeFilterFunction logResponse() {
    return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        log.info("Response: {}", clientResponse.headers().asHttpHeaders().get("property-header"));
        return Mono.just(clientResponse);
    });
}
user5515
  • 277
  • 4
  • 8
  • 4
    I know about this but this does not generate a log that contains request information AND response information in the same log. I'm looking a way to log both at the same time. This answer generates one log for request and one log for response. – nsphung Oct 24 '19 at 19:04
  • 4
    You cannot log a request body with an Exchange Filter – Stefan Haberl Jun 30 '20 at 08:09
  • @StefanHaberl yes you can, using the `.ofRequestProcessor()` in function `ExchangeFilterFunction` – aswzen Nov 12 '21 at 09:41
  • 1
    @aswzen may you share code on how one may log request bodies using ExchangeFilterFunction.ofRequestProcessor()? – Sami Jun 12 '22 at 20:27
  • @Sami `public static ExchangeFilterFunction logRequest(final StringBuilder logTrail) { return ExchangeFilterFunction.ofRequestProcessor(request -> { //request.body() return Mono.just(request); }); }` – aswzen Jun 13 '22 at 02:39
0

I don't think you can call .bodyToMono twice (once in the filter and then again where you use the client), so you might not be able to log that in the filter. As for the other details...

The WebClient config:

@Configuration
class MyClientConfig {

    @Bean
    fun myWebClient(): WebClient {
        return WebClient
            .builder()
            .baseUrl(myUrl)
            .filter(MyFilter())
            .build()
    }
}

The filter:

class MyFilter : ExchangeFilterFunction {

    override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
        return next.exchange(request).flatMap { response ->

            // log whenever you want here...
            println("request: ${request.url()}, response: ${response.statusCode()}")

            Mono.just(response)
        }
    }
}
adanilev
  • 3,008
  • 3
  • 15
  • 20