web-dev-qa-db-ja.com

Spring 5 WebClient呼び出しを記録する方法

Spring 5 WebClientを使用してリクエストを記録しようとしています。どうすればそれを達成できますか?

(Spring 5とSpring Boot 2を使用しています)

現時点では、コードは次のようになっています。

try {
    return webClient.get().uri(url, urlParams).exchange().flatMap(response -> response.bodyToMono(Test.class))
            .map(test -> xxx.set(test));
} catch (RestClientException e) {
    log.error("Cannot get counter from opus", e);
    throw e;
}
23
Seb

ExchangeFilterFunction を使用して簡単に実行できます

_WebClient.Builder_を使用してlogRequestを作成するときに、カスタムWebClientフィルターを追加するだけです。

そのようなフィルターの例と、それをWebClientに追加する方法を以下に示します。

_@Slf4j
@Component
public class MyClient {

    private final WebClient webClient;

    // Create WebClient instance using builder.
    // If you use spring-boot 2.0, the builder will be autoconfigured for you
    // with the "prototype" scope, meaning each injection point will receive
    // a newly cloned instance of the builder.
    public MyClient(WebClient.Builder webClientBuilder) {
        webClient = webClientBuilder // you can also just use WebClient.builder()
                .baseUrl("https://httpbin.org")
                .filter(logRequest()) // here is the magic
                .build();
    }

    // Just example of sending request
    public void send(String path) {
        ClientResponse clientResponse = webClient
                .get().uri(uriBuilder -> uriBuilder.path(path)
                        .queryParam("param", "value")
                        .build())
                .exchange()
                .block();
        log.info("Response: {}", clientResponse.toEntity(String.class).block());
    }

    // This method returns filter function which will log request data
    private static ExchangeFilterFunction logRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
            log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
            clientRequest.headers().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return Mono.just(clientRequest);
        });
    }

}
_

次に、myClient.send("get");を呼び出すだけで、ログメッセージが表示されます。

出力例:

_Request: GET https://httpbin.org/get?param=value
header1=value1
header2=value2
_
20

必ずしも独自のロガーをロールする必要はありません。reactor.ipc.netty.channel.ChannelOperationsHandlerが自動的に行います。そのクラスのロギングシステムをDEBUGレベルでログするように設定するだけです。

2017-11-23 12:52:04.562 DEBUG 41449 --- [ctor-http-nio-5] r.i.n.channel.ChannelOperationsHandler   : [id: 0x9183d6da, L:/127.0.0.1:57681 - R:localhost/127.0.0.1:8000] Writing object DefaultFullHttpRequest(decodeResult: success, version: HTTP/1.1, content: UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeHeapByteBuf(ridx: 0, widx: 0, cap: 0))
GET /api/v1/watch/namespaces/default/events HTTP/1.1
user-agent: ReactorNetty/0.7.1.RELEASE
Host: localhost:8000
accept-encoding: gzip
Accept: application/json
content-length: 0

バグを減らす1つの方法は、可能な限りコードを記述しないことです。

2018年11月

spring-webflux:5.1.2.RELEASEを使用すると、上記は機能しなくなります。代わりに次を使用してください。

logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=DEBUG
...
2018-11-06 20:58:58.181 DEBUG 20300 --- [           main] o.s.w.r.f.client.ExchangeFunctions       : [2026fbff] HTTP GET http://localhost:8080/stocks/search?symbol=AAPL
2018-11-06 20:58:58.451 DEBUG 20300 --- [ctor-http-nio-4] o.s.w.r.f.client.ExchangeFunctions       : [2026fbff] Response 400 BAD_REQUEST

ヘッダーまたはフォーム本文を記録するには、上記をTRACEレベルに設定します。ただし、それだけでは不十分です。

ExchangeStrategies exchangeStrategies = ExchangeStrategies.withDefaults();
exchangeStrategies
    .messageWriters().stream()
    .filter(LoggingCodecSupport.class::isInstance)
    .forEach(writer -> ((LoggingCodecSupport)writer).setEnableLoggingRequestDetails(true));

client = WebClient.builder()
    .exchangeStrategies(exchangeStrategies)

2019年3月

リクエストとレスポンスの本文を記録する方法を尋ねたコメントの質問に答えて、Springがそのようなロガーを持っているが、WebClientがNetty上に構築されているのかわかりません。したがって、パッケージreactor.ipc.nettyは、 this answerとともに動作するはずです。

22
Abhijit Sarkar

体を記録したくない場合、これは本当に簡単です。

Spring Boot> = 2.1.0

Application.propertiesに次を追加します。

logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE
spring.http.log-request-details=true

2行目では、ヘッダーがログに含まれます。

Spring Boot <2.1.0

Application.propertiesに次を追加します。

logging.level.org.springframework.web.reactive.function.client.ExchangeFunctions=TRACE

上記の2行目の代わりに、次のようなクラスを宣言する必要があります。

@Configuration
static class LoggingCodecConfig {

    @Bean
    @Order(0)
    public CodecCustomizer loggingCodecCustomizer() {
        return (configurer) -> configurer.defaultCodecs()
                .enableLoggingRequestDetails(true);
    }

}

このブライアンクロゼルの回答 の礼儀

9
Fletch

このようにSpring WebClientを作成すると、盗聴オプションが有効になり、盗聴するように要求することで、nettyに要求/応答のログを記録させることができます。

        WebClient webClient = WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(
                HttpClient.create().wiretap(true)
            ))
            .build()

そして、あなたのロギングのセットアップがあります:

logging.level.reactor.netty.http.client.HttpClient: DEBUG

これにより、要求/応答のすべて(本文を含む)がログに記録されますが、この形式はHTTPに固有のものではないため、読みにくくなっています。

8
Matthew Buckett