一、场景介绍
在通常情况下,微服务的业务功能都需要通过网关来进行转发,记录业务响应日志通过网关来记录是最有利的方式。
二、演示代码
-
测试版本
组件名 版本号 Spring Boot 2.2.5.RELEASE Spring Cloud Hoxton.SR3 Spring Cloud Alibaba 2.2.1.RELEASE -
测试代码
import com.google.common.base.Joiner; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.Charset; import java.util.List; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR; /** * @ClassName: CustomResponseGlobalLogFilter * @Description: 自定义全局请求响应日志记录 * @Author: Rambo * @CreateDate: 2020/8/6 17:05 * @UpdateUser: Rambo * @UpdateDate: 2020/8/6 17:05 * @Version: 1.0.0 */ @Component @Slf4j public class CustomResponseGlobalLogFilter implements GlobalFilter, Ordered { private static Joiner joiner = Joiner.on(""); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 获取响应对象 ServerHttpResponse response = exchange.getResponse(); DataBufferFactory bufferFactory = response.bufferFactory(); ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(response) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { // 获取响应 ContentType String responseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR); // 记录 JSON 格式数据的响应体 if (!StringUtils.isEmpty(responseContentType) && responseContentType.contains(MediaType.APPLICATION_JSON_VALUE)) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); // 解决返回体分段传输 return super.writeWith(fluxBody.buffer().map(dataBuffers -> { List<String> list = Lists.newArrayList(); dataBuffers.forEach(dataBuffer -> { byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer); list.add(new String(content, Charset.forName("UTF-8"))); }); String responseData = joiner.join(list); log.info("<------------------------ RESPONSE RESULT = [{}]", responseData.replaceAll("\n","").replaceAll("\t","")); return bufferFactory.wrap(responseData.getBytes()); })); } } return super.writeWith(body); } }; return chain.filter(exchange.mutate().response(responseDecorator).build()); } @Override public int getOrder() { // 必须要小于 -1 return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER-1; } }