diff --git a/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java new file mode 100644 index 0000000..7765494 --- /dev/null +++ b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java @@ -0,0 +1,132 @@ + +package com.sczx.gateway.filter; + +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.ServerWebExchangeDecorator; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +/** + * 全局日志过滤器,用于打印请求路径、Header、Body 和响应状态码、Body。 + * 适用于 Spring Boot 2.3.x + Spring Cloud Hoxton.SR12 环境。 + */ +@Slf4j +@Component +public class LoggingGlobalFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 包装请求体 + RequestBodyLogExchange requestLogExchange = new RequestBodyLogExchange(exchange); + // 包装响应体 + ResponseBodyLogExchange responseLogExchange = new ResponseBodyLogExchange(exchange); + + return chain.filter(requestLogExchange) + .then(Mono.fromRunnable(() -> { + String requestBody = requestLogExchange.getRequestBody(); + String responseBody = responseLogExchange.getResponseBody(); + + log.info("🌐 请求路径: {}", exchange.getRequest().getPath()); + log.info("📥 请求头: {}", exchange.getRequest().getHeaders()); + log.info("📥 请求体: {}", requestBody); + log.info("🔚 响应状态: {}", exchange.getResponse().getStatusCode()); + log.info("📤 响应内容: {}", responseBody); + })); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; // 最后执行 + } + + /** + * 请求体拦截装饰器 + */ + static class RequestBodyLogExchange extends ServerWebExchangeDecorator { + + private final StringBuilder requestBody = new StringBuilder(); + + public RequestBodyLogExchange(ServerWebExchange delegate) { + super(delegate); + } + + @Override + public ServerHttpRequest getRequest() { + return new ServerHttpRequestDecorator(super.getRequest()) { + @Override + public Flux getBody() { + return super.getBody().map(buffer -> { + byte[] bytes = new byte[buffer.readableByteCount()]; + buffer.read(bytes); + String chunk = new String(bytes, StandardCharsets.UTF_8); + requestBody.append(chunk); + DataBufferUtils.release(buffer); // 释放原始 buffer + return buffer.factory().wrap(bytes); // 返回新的 buffer + }); + } + }; + } + + public String getRequestBody() { + return requestBody.toString(); + } + } + + /** + * 响应体拦截装饰器(适配 Spring Boot 2.3.x) + */ + static class ResponseBodyLogExchange extends ServerWebExchangeDecorator { + + private final StringBuilder responseBody = new StringBuilder(); + private final ServerWebExchange exchange; // 显式保存 exchange 引用 + + public ResponseBodyLogExchange(ServerWebExchange delegate) { + super(delegate); + this.exchange = delegate; + } + + @Override + public ServerHttpResponse getResponse() { + return new ServerHttpResponseDecorator(super.getResponse()) { + @Override + public Mono writeWith(Publisher body) { + if (body instanceof Flux) { + Flux fluxBody = (Flux) body; + return super.writeWith(fluxBody.map(buffer -> { + byte[] content = new byte[buffer.readableByteCount()]; + buffer.read(content); + DataBufferUtils.release(buffer); // 释放原始 buffer + + String responseStr = new String(content, StandardCharsets.UTF_8); + responseBody.append(responseStr); + + // ✅ 使用外部传入的 exchange 来设置 attribute + exchange.getAttributes().put("responseBody", responseStr); + + return buffer.factory().wrap(content); // 返回新 buffer + })); + } + return super.writeWith(body); + } + }; + } + + public String getResponseBody() { + return responseBody.toString(); + } + } +} \ No newline at end of file