如何记录春季5 Web客户端调用

2022-08-31 13:33:51

我正在尝试使用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;
}

答案 1

您可以使用 ExchangeFilterFunction 轻松完成此操作

只需在创建 using 时添加自定义筛选器即可。logRequestWebClientWebClient.Builder

下面是此类筛选器的示例以及如何将其添加到 .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

编辑

有些人在评论中指出这是不好的做法等。我想澄清一下:在这里打电话只是为了演示目的。请求日志记录筛选器仍将正常工作。您无需添加到代码中即可工作。您可以使用 以通常的方式执行 http 调用,链接方法并返回堆栈,直到有人订阅它。答案的唯一相关部分是过滤器。您可以完全忽略方法 - 它不是解决方案的一部分 - 它只是证明过滤器的工作原理。block()block()block()ExchangeFilterFunctionWebClientMonologRequest()send()

有些人还询问如何记录响应。要记录响应,您可以编写另一个响应并将其添加到 。您可以使用 ExchangeFilterFunction.ofResponseProcessor 帮助程序来实现此目的,方法与 使用方式相同。您可以使用 ClientResponse 的方法获取标头/Cookie 等。ExchangeFilterFunctionWebClientExchangeFilterFunction.ofRequestProcessor

    // 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的答案来获得这个想法。


答案 2

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

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

然后设置日志记录:

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

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


推荐