Adding a Wiretap to a Spring WebFlux WebClient to Log All Request/Response Data

Featured image for sharing metadata for article

If you're working with Spring Boot Webflux, you'll likely be using the WebClient as your HTTP client.

There may be cases that you need to add debug logging to investigate requests over the wire, for instance to debug things locally.

We can do this by taking a configuration for our WebClient:

import io.netty.handler.logging.LogLevel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.logging.AdvancedByteBufFormat;

@Configuration
public class WebClientConfiguration {

  @Bean
  public WebClient webClient(
      WebClient.Builder webClientBuilder,
      @Value("https://path/to/api") String apiBaseUrl,
      ) {
    return webClientBuilder
        .baseUrl(apiBaseUrl)
        .build();
  }
}

Then, to turn on our debug logging, we can provide an HttpClient that has been wiretap'd:

import io.netty.handler.logging.LogLevel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.logging.AdvancedByteBufFormat;

@Configuration
public class WebClientConfiguration {

  @Bean
  public WebClient webClient(
      WebClient.Builder webClientBuilder,
      @Value("https://path/to/api") String apiBaseUrl,
      ) {
    HttpClient httpClient =
        HttpClient.create()
            .wiretap(
                this.getClass().getCanonicalName(), LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL);

    return webClientBuilder
        .clientConnector(new ReactorClientHttpConnector(httpClient))
        .baseUrl(apiBaseUrl)
        .build();
  }
}

The wiretap takes note of logging interfaces on the classpath, and for instance will use SLF4J if it finds it available, so logs will go to the location and format you want them in.

You may want to turn this on depending on whether we're running in debug mode, for instance:

import io.netty.handler.logging.LogLevel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.logging.AdvancedByteBufFormat;

@Configuration
public class WebClientConfiguration {

  @Bean
  public boolean isDebug(
      @Value("#{environment.getProperty('debug') != null && environment.getProperty('debug') != 'false'}")
          boolean isDebug) {
    return isDebug;
  }

  @Bean
  @ConditionalOnExpression("#{isDebug == true}")
  public ReactorClientHttpConnector wiretappedConnector(boolean isDebug) {
    HttpClient httpClient =
        HttpClient.create()
            .wiretap(
                this.getClass().getCanonicalName(), LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL);
    return new ReactorClientHttpConnector(httpClient);
  }

  @Bean
  public WebClient webClient(
      WebClient.Builder webClientBuilder,
      @Value("https://path/to/api") String apiBaseUrl,
      ReactorClientHttpConnector reactorClientHttpConnector) {
    return webClientBuilder.clientConnector(reactorClientHttpConnector).baseUrl(apiBaseUrl).build();
  }
}

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #java #spring-boot #spring.

Also on:

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.