1. 概要

このチュートリアルでは、SpringのWebClient(リアクティブHTTPクライアント)をカスタマイズして、要求と応答をログに記録する方法を示します。

2. WebClient

WebClient は、 Spring WebFlux に基づく、HTTPリクエスト用のリアクティブで非ブロッキングのインターフェースです。 宣言型構成用のリアクティブタイプを備えた機能的で流暢なAPIを備えています。

舞台裏では、WebClientがHTTPクライアントを呼び出します。 Reactor Nettyはデフォルトであり、JettyのリアクティブHttpClientもサポートされています。 さらに、WebClient用にClientConnectorを設定することにより、HTTPクライアントの他の実装をプラグインすることができます。

3. リクエストとレスポンスのロギング

WebClientによって使用されるデフォルトのHttpClientはNetty実装であるため、reactor.netty.http.clientのログレベルをDEBUGに変更すると、ロギングを要求しますが、カスタマイズされたログが必要な場合は、 WebClient#filtersを介してロガーを構成できます。

WebClient
  .builder()
  .filters(exchangeFilterFunctions -> {
      exchangeFilterFunctions.add(logRequest());
      exchangeFilterFunctions.add(logResponse());
  })
  .build()

このコードスニペットでは、リクエストとレスポンスをログに記録するための2つの個別のフィルターを追加しました。

ExchangeFilterFunction#ofRequestProcessor を使用して、logRequestを実装しましょう。

ExchangeFilterFunction logRequest() {
    return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
        if (log.isDebugEnabled()) {
            StringBuilder sb = new StringBuilder("Request: \n");
            //append clientRequest method and url
            clientRequest
              .headers()
              .forEach((name, values) -> values.forEach(value -> /* append header key/value */));
            log.debug(sb.toString());
        }
        return Mono.just(clientRequest);
    });
}

logResponse 同じです、 ただし、代わりにExchangeFilterFunction#ofResponseProcessorを使用する必要があります。

これで、reactor.netty.http.clientログレベルをINFOまたはERRORに変更して、よりクリーンな出力を得ることができます。

4. リクエストとレスポンスを本文で記録

HTTPクライアントには、要求と応答の本文をログに記録する機能があります。 したがって、 目標を達成するために、WebClientでログ対応のHTTPクライアントを使用します。

これを行うには、 WebClient.Builder# clientConnector –JettyおよびNettyHTTPクライアントを手動で設定します。

4.1. Jetty HttpClientを使用したロギング

まず、jetty-reactive-httpclientのMaven依存関係をpomに追加しましょう。

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-reactive-httpclient</artifactId>
    <version>1.1.6</version>
</dependency>

次に、カスタマイズされたJetty HttpClientを作成します。

SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
HttpClient httpClient = new HttpClient(sslContextFactory) {
    @Override
    public Request newRequest(URI uri) {
        Request request = super.newRequest(uri);
        return enhance(request);
    }
};

ここでは、 HttpClient#newRequest をオーバーライドしてから、Requestをログエンハンサーでラップしました。

次に、リクエストの各部分が利用可能になったときにログに記録できるように、イベントをリクエストに登録する必要があります。

Request enhance(Request request) {
    StringBuilder group = new StringBuilder();
    request.onRequestBegin(theRequest -> {
        // append request url and method to group
    });
    request.onRequestHeaders(theRequest -> {
        for (HttpField header : theRequest.getHeaders()) {
            // append request headers to group
        }
    });
    request.onRequestContent((theRequest, content) -> {
        // append content to group
    });
    request.onRequestSuccess(theRequest -> {
        log.debug(group.toString());
        group.delete(0, group.length());
    });
    group.append("\n");
    request.onResponseBegin(theResponse -> {
        // append response status to group
    });
    request.onResponseHeaders(theResponse -> {
        for (HttpField header : theResponse.getHeaders()) {
            // append response headers to group
        }
    });
    request.onResponseContent((theResponse, content) -> {
        // append content to group
    });
    request.onResponseSuccess(theResponse -> {
        log.debug(group.toString());
    });
    return request;
}

最後に、WebClientインスタンスを構築する必要があります。

WebClient
  .builder()
  .clientConnector(new JettyClientHttpConnector(httpClient))
  .build()

もちろん、前と同じように、RequestLogEnhancerのログレベルをDEBUGに設定する必要があります。

4.2. Netty HttpClientを使用したロギング

まず、Netty HttpClientを作成しましょう。

HttpClient httpClient = HttpClient
  .create()
  .wiretap(true)

ワイヤータップを有効にすると、各要求と応答が完全に詳細に記録されます。

次に、Nettyのクライアントパッケージreactor。netty.http.clientのログレベルをDEBUGに設定する必要があります。

logging.level.reactor.netty.http.client=DEBUG

それでは、WebClientを作成しましょう。

WebClient
  .builder()
  .clientConnector(new ReactorClientHttpConnector(httpClient))
  .build()

WebClient は、すべてのリクエストとレスポンスを詳細に記録しますが、 Nettyのデフォルト形式の組み込みロガーには、本文の16進表現とテキスト表現の両方と、リクエストに関する多くのデータが含まれています。および応答イベント。

したがって、Nettyのテキストロガーのみが必要な場合は、HttpClientを構成できます。

HttpClient httpClient = HttpClient
  .create()
  .wiretap("reactor.netty.http.client.HttpClient", 
    LogLevel.DEBUG, AdvancedByteBufFormat.TEXTUAL);

5. 結論

このチュートリアルでは、Spring WebClient を使用しながら、要求および応答データをログに記録するためのいくつかの手法を使用しました。

いつものように、コードはGitHubから入手できます。