如何记录 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;
}

原文由 Seb 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

您可以使用 ExchangeFilterFunction 轻松完成

只需在创建 WebClient 使用 WebClient.Builder 时添加自定义 logRequest 过滤器。

这是此类过滤器的示例以及如何将其添加到 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. This method is NOT part of the answer
    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

编辑

有人在评论中指出 block() 是不好的做法等。我想澄清一下: block() 此处调用仅 _用于演示目的_。无论如何,请求日志记录过滤器都会起作用。您无需将 block() 添加到代码中即可使 ExchangeFilterFunction 工作。您可以使用 WebClient 以通常的方式执行 http 调用,链接方法并返回 Mono 直到有人订阅它。答案唯一相关的部分是 logRequest() 过滤器。您可以完全忽略 send() 方法 - 它不是解决方案的一部分 - 它只是证明过滤器有效。

有些人还询问如何记录响应。要记录响应,您可以编写另一个 ExchangeFilterFunction 并将其添加到 WebClient 。为此,您可以使用 ExchangeFilterFunction.ofResponseProcessor 助手,就像使用 ExchangeFilterFunction.ofRequestProcessor 一样。您可以使用 ClientResponse 的方法来获取标头/cookie 等。

     // This method returns filter function which will log response data
    private static ExchangeFilterFunction logResponse() {
        return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
            log.info("Response status: {}", clientResponse.statusCode());
            clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{}={}", name, value)));
            return Mono.just(clientResponse);
        });
    }

不要忘记将它添加到您的 WebClient

 .filter(logResponse())

但要小心,不要尝试在过滤器中读取响应主体。由于它的流性质,在没有某种缓冲包装器的情况下,主体只能被消耗一次。因此,如果您将在过滤器中阅读它,您将无法在订阅者中阅读它。

如果你真的需要记录正文,你可以让底层(Netty)来做这件事。请参阅 Matthew Buckett 的回答 以了解这个想法。

原文由 Ruslan Stelmachenko 发布,翻译遵循 CC BY-SA 4.0 许可协议

您可以让 netty 通过要求它进行窃听来记录请求/响应,如果您像这样创建 Spring WebClient,那么它会启用窃听选项。

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

然后进行日志记录设置:

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

这将记录请求/响应的所有内容(包括正文),但格式并非特定于 HTTP,因此可读性差。

原文由 Matthew Buckett 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题