如何为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
相关产品推荐
相关产品推荐

