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

如何为Spring WebClient添加自定义格式的请求响应日志?

WebClient自定义格式日志记录方案(请求/响应头+消息体)

核心问题说明

WebClient的请求/响应体是流式的Flux<DataBuffer>,只能被消费一次,直接读取会导致后续请求/响应处理失败。要实现带自定义前缀的日志,需要先缓存体数据,再重新生成流供后续使用。

完整实现代码

自定义ExchangeFilterFunction(合并请求+响应日志)

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.client.reactive.ClientRequest;
import org.springframework.http.client.reactive.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import io.netty.buffer.ByteBufAllocator;

private ExchangeFilterFunction logRequestResponse() {
    return (clientRequest, next) -> {
        // 打印请求头(自定义前缀)
        System.out.println("Request Headers: " + clientRequest.headers());

        // 缓存请求体并打印
        return DataBufferUtils.join(clientRequest.body())
                .map(reqDataBuffer -> {
                    byte[] reqBytes = new byte[reqDataBuffer.readableByteCount()];
                    reqDataBuffer.read(reqBytes);
                    DataBufferUtils.release(reqDataBuffer);
                    // 打印请求体(自定义前缀)
                    System.out.println("Request Body: " + new String(reqBytes, StandardCharsets.UTF_8));

                    // 重新生成请求体流,保证原请求正常发送
                    ClientRequest newRequest = clientRequest.mutate()
                            .body(Flux.just(ByteBufAllocator.DEFAULT.buffer().writeBytes(reqBytes)))
                            .build();

                    // 处理响应日志
                    return next.exchange(newRequest)
                            .flatMap(clientResponse -> {
                                // 打印响应头(自定义前缀)
                                System.out.println("Response Headers: " + clientResponse.headers().asHttpHeaders());

                                // 缓存响应体并打印
                                return clientResponse.bodyToMono(DataBuffer.class)
                                        .map(resDataBuffer -> {
                                            byte[] resBytes = new byte[resDataBuffer.readableByteCount()];
                                            resDataBuffer.read(resBytes);
                                            DataBufferUtils.release(resDataBuffer);
                                            // 打印响应体(自定义前缀)
                                            System.out.println("Response Body: " + new String(resBytes, StandardCharsets.UTF_8));

                                            // 重新生成响应体流,保证后续处理正常
                                            return ClientResponse.from(clientResponse)
                                                    .body(Flux.just(ByteBufAllocator.DEFAULT.buffer().writeBytes(resBytes)))
                                                    .build();
                                        });
                            });
                });
    };
}

配置WebClient

将自定义Filter注入WebClient构建器:

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

WebClient webClient = WebClient.builder()
        .filter(logRequestResponse())
        .build();

注意事项

  • 大文件场景优化:如果处理大体积请求/响应,缓存整个体数据会占用过多内存,可改为只记录前N个字节,或跳过二进制内容的打印。
  • 二进制内容处理:对于文件上传、下载等二进制请求/响应,不要直接转成String,可改为记录字节长度或标记为[Binary Content]。
  • 日志框架适配:替换System.out.println为SLF4J等日志框架的log.info()方法,可实现更灵活的日志级别控制和持久化。
  • 路径过滤:可在Filter中通过clientRequest.url()判断,只记录特定路径的请求日志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:12:09