Logging Spring WebClient Calls – 记录Spring WebClient的调用

最后修改: 2019年 9月 7日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we are going to show how to customize Spring’s WebClient – a reactive HTTP client – to log requests and responses.

在本教程中,我们将展示如何定制Spring的WebClient–一个反应式HTTP客户端,以记录请求和响应。

2. WebClient

2.WebClient

WebClient is a reactive and non-blocking interface for HTTP requests, based on Spring WebFlux. It has a functional, fluent API with reactive types for declarative composition.

WebClient是一个用于HTTP请求的反应式和非阻塞接口,基于Spring WebFlux。它有一个功能性的、流畅的API,具有用于声明式组合的反应式类型。

Behind the scenes, WebClient calls an HTTP client. Reactor Netty is the default and reactive HttpClient of Jetty is also supported. Moreover, it’s possible to plug other implementations of HTTP client by setting up a ClientConnector for WebClient.

在幕后,WebClient调用一个HTTP客户端。Reactor Netty是默认的,Jetty的反应式HttpClient也被支持。此外,通过为WebClient设置ClientConnector,可以插入其他HTTP客户端的实现。

3. Logging Requests and Responses

3.记录请求和回应

The default HttpClient used by WebClient is the Netty implementation, so after we change the reactor.netty.http.client logging level to DEBUG, we can see some request logging, but if we need a customized log, we can configure our loggers via WebClient#filters:

WebClient使用的默认HttpClient是Netty的实现,所以在我们将reactor.netty.http.client的日志级别改为DEBUG之后,我们可以看到一些请求日志。客户端日志级别改为DEBUG后,我们可以看到一些请求日志,但如果我们需要自定义的日志,我们可以通过WebClient#filters配置e我们的日志器。

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

In this code snippet, we’ve added two separate filters to log the request and the response.

在这段代码中,我们添加了两个单独的过滤器来记录请求和响应。

Let’s implement logRequest by using ExchangeFilterFunction#ofRequestProcessor:

让我们通过使用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 is the same, but we have to use ExchangeFilterFunction#ofResponseProcessor instead.

logResponse是相同的,但我们必须使用ExchangeFilterFunction#ofResponseProcessor来代替。

Now we can change the reactor.netty.http.client log level to INFO or ERROR to have a cleaner output.

现在我们可以把reactor.netty.http.client日志级别改为INFOERROR,以便有一个更干净的输出。

4. Logging Request and Response with Body

4.记录请求和响应与正文

HTTP clients have features to log the bodies of requests and responses. Thus, to achieve the goal, we are going to use a log-enabled HTTP client with our WebClient.

HTTP客户端具有记录请求和响应正文的功能。因此,为了实现这一目标,我们要在我们的WebClient中使用一个支持日志的HTTP客户端。

We can do this by manually setting WebClient.Builder#clientConnector – let’s see with Jetty and Netty HTTP clients.

我们可以通过手动设置WebClient.Builder#clientConnector – 让我们看看与Jetty和Netty HTTP客户端。

4.1. Logging with Jetty HttpClient

4.1.使用Jetty HttpClient进行日志记录

First, let’s add the Maven dependency for jetty-reactive-httpclient to our pom:

首先,让我们把jetty-reactive-httpclient的Maven依赖性添加到我们的pom。

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

Then we’re going to create a customized Jetty HttpClient:

然后我们要创建一个定制的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);
    }
};

Here, we’ve overridden HttpClient#newRequest, then wrapped the Request in a log enhancer.

在这里,我们重写了HttpClient#newRequest,然后将Request包裹在一个日志增强器中。

Next, we need to register events with the request so that we can log as each part of the request becomes available:

接下来,我们需要对请求进行事件注册,这样我们就可以在请求的每一部分变得可用时进行记录。

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;
}

Finally, we have to build the WebClient instance:

最后,我们必须建立WebClient实例。

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

Of course, as we did before, we’ll need to set the log level of RequestLogEnhancer to DEBUG.

当然,正如我们之前所做的,我们需要将RequestLogEnhancer的日志级别设置为DEBUG

4.2. Logging with Netty HttpClient

4.2.使用Netty HttpClient进行日志记录

First, let’s create a Netty HttpClient:

首先,让我们创建一个Netty HttpClient

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

Having enabled the wiretap, each request and response will be logged in full detail.

启用窃听后,每个请求和响应都将被完整地记录下来。

Next, we have to set the log level of Netty’s client package reactor.netty.http.client to DEBUG:

接下来,我们要把Netty的客户端包reactor.netty.http.client的日志级别设为DEBUG

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

Now, let’s build the WebClient:

现在,让我们来构建WebClient

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

Our WebClient will log every request and response in full detail, but the default format of Netty built-in logger contains both Hex and Text representation of bodies and also a lot of data about request and response events.

我们的WebClient将完整地记录每一个请求和响应,但Netty内置日志器的默认格式包含十六进制和文本表示的机构,也包含大量关于请求和响应事件的数据。

So, if we need only the text logger for Netty, we can configure the HttpClient:

因此,如果我们只需要Netty的文本记录器,我们可以配置HttpClient

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

5. Conclusion

5.总结

In this tutorial, we’ve used several techniques for logging request and response data while using Spring WebClient.

在本教程中,我们在使用Spring WebClient时使用了几种技术来记录请求和响应数据。

As always the code is available over on GitHub.

像往常一样,代码可在GitHub上获得。