You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Reactor Netty HttpClient一次性完整记录响应日志?

解决WebClient响应日志分块混乱的问题

要实现响应完全就绪后一次性记录完整日志,别用HttpClient的wiretap了——它是Reactor Netty底层IO层面的日志,天生就是按分块读取的时机输出,没法改成一次性输出。我们可以在WebClient层面自定义拦截器,手动收集完整响应后再打日志。

具体实现步骤

  • 自定义ExchangeFilterFunction,拦截请求和响应:
    1. 让请求正常发送,拿到响应后读取完整的响应体内容
    2. 记录完整的请求和响应日志
    3. 把读取过的响应体重新封装回去(因为DataBuffer是一次性消费的,必须缓存后再返回,否则后续业务拿不到响应体)
  • 将自定义过滤器配置到WebClient中

代码示例

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

public class FullResponseLoggingFilter implements ExchangeFilterFunction {

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        // 按需记录请求基础信息
        String requestLog = String.format("请求方法: %s, 请求URL: %s",
                request.method(), request.url());

        return next.exchange(request)
                .flatMap(response -> {
                    // 读取完整响应体
                    return DataBufferUtils.join(response.body())
                            .map(dataBuffer -> {
                                byte[] bodyBytes = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(bodyBytes);
                                DataBufferUtils.release(dataBuffer);
                                String responseBody = new String(bodyBytes, StandardCharsets.UTF_8);

                                // 输出完整响应日志
                                String responseLog = String.format("响应状态码: %s, 响应体: %s",
                                        response.statusCode(), responseBody);
                                // 替换成你项目的日志框架(如SLF4J的logger)
                                System.out.println(requestLog);
                                System.out.println(responseLog);

                                // 重新封装响应体,保证后续业务能正常获取
                                DataBuffer newBuffer = response.bufferFactory().wrap(bodyBytes);
                                return ClientResponse.from(response)
                                        .body(Mono.just(newBuffer))
                                        .build();
                            });
                });
    }
}

配置WebClient时添加过滤器:

import org.springframework.web.reactive.function.client.WebClient;

WebClient webClient = WebClient.builder()
        .filter(new FullResponseLoggingFilter())
        // 其他配置(如baseUrl、编码等)
        .baseUrl("https://your-target-url.com")
        .build();

注意事项

  • 如果响应体特别大(如几十MB以上),一次性读取到内存可能引发OOM,需根据业务场景评估是否适用,或添加长度限制(超过阈值则只记录部分内容)
  • 日志输出请替换为项目实际使用的日志框架(如SLF4J),不要直接用System.out

内容的提问来源于stack exchange,提问作者Deekshith Anand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 15:32:34