需要使用请求装饰类和响应装饰类,把请求体和响应体保存一下,再在全局Post过滤器里面获得该请求体。
请求装饰类
package com.chilun.apiopenspace.gateway.filter;import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
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.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;import java.nio.charset.StandardCharsets;/*** @author 齿轮* @date 2024-02-29-17:51*/
@Slf4j
public class LoggingRequestDecorator extends ServerHttpRequestDecorator {private final String uuid;private final DataBufferFactory bufferFactory;public LoggingRequestDecorator(ServerHttpRequest delegate, ServerWebExchange exchange, String uuid) {super(delegate);this.bufferFactory = exchange.getResponse().bufferFactory();this.uuid = uuid;}@NotNull@Overridepublic Flux<DataBuffer> getBody() {Flux<DataBuffer> originalBody = super.getBody();return originalBody.map(dataBuffer -> {byte[] content = new byte[dataBuffer.readableByteCount()];dataBuffer.read(content);// 将请求体数据转换为字符串,并保存到缓存中String requestBody = new String(content, StandardCharsets.UTF_8);log.info("Request Body: " + requestBody);LogCacheMap.saveRequest(uuid, requestBody);//重新包装了DataBuffer,所以要释放原始的DataBufferDataBufferUtils.release(dataBuffer);// 返回新的DataBufferreturn bufferFactory.wrap(content);});}
}
响应装饰类
package com.chilun.apiopenspace.gateway.filter;import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;/*** @author 齿轮* @date 2024-02-29-17:51*/
@Slf4j
public class LoggingResponseDecorator extends ServerHttpResponseDecorator {private final DataBufferFactory bufferFactory;private final String uuid;public LoggingResponseDecorator(ServerHttpResponse delegate, String uuid) {super(delegate);this.bufferFactory = delegate.bufferFactory();this.uuid = uuid;}@Overridepublic Mono<Void> writeWith(org.reactivestreams.Publisher<? extends org.springframework.core.io.buffer.DataBuffer> body) {
// if (getStatusCode().is2xxSuccessful()) {
// //正常情况直接返回
// return super.writeWith(body);
// } else if (body instanceof Flux) {//异常情况保存响应Flux<? extends DataBuffer> fluxBody = Flux.from(body);return super.writeWith(fluxBody.buffer().map(dataBuffers -> {DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer join = dataBufferFactory.join(dataBuffers);byte[] content = new byte[join.readableByteCount()];join.read(content);DataBufferUtils.release(join);String responseBody = new String(content, StandardCharsets.UTF_8);log.info("response body: {}", responseBody);LogCacheMap.saveResponse(uuid, responseBody);byte[] uppedContent = responseBody.replaceAll(":null", ":\"\"").getBytes(StandardCharsets.UTF_8);return bufferFactory.wrap(uppedContent);}));}return super.writeWith(body);}
}
用到的自定义工具类
package com.chilun.apiopenspace.gateway.Utils;import java.util.HashMap;/*** @author 齿轮* @date 2024-02-29-18:52*/public class LogCacheMap {public static HashMap<String, String> LogMap = new HashMap<>();public static void saveRequest(String uuid, String request) {LogMap.put(uuid + "request", request);}public static String getRequest(String uuid) {return LogMap.remove(uuid + "request");}public static void saveResponse(String uuid, String response) {LogMap.put(uuid + "response", response);}public static String getResponse(String uuid) {return LogMap.remove(uuid + "response");}
}
全局过滤器类
package com.chilun.apiopenspace.gateway.filter;import com.chilun.apiopenspace.gateway.Utils.LogCacheMap;
import com.chilun.apiopenspace.gateway.service.AccessLogService;
import com.google.common.base.Joiner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import javax.annotation.Resource;
import java.util.UUID;/*** @author 齿轮* @date 2024-02-28-14:35*/
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {@ResourceAccessLogService logService;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String uuid = UUID.randomUUID().toString();//注入requestDecorator、responseDecorator用于获得请求体、响应体ServerHttpRequestDecorator requestDecorator = new LoggingRequestDecorator(exchange.getRequest(), exchange, uuid);ServerHttpResponseDecorator responseDecorator = new LoggingResponseDecorator(exchange.getResponse(), uuid);return chain.filter(exchange.mutate().request(requestDecorator).response(responseDecorator).build()).then(Mono.just(exchange)).map(serverWebExchange -> {if (serverWebExchange.getResponse().getStatusCode().is2xxSuccessful()) {logService.sendCommonLog(true);logService.sendRightLog(LogCacheMap.getRequest(uuid), LogCacheMap.getResponse(uuid));} else {logService.sendCommonLog(false);logService.sendErrorLog(LogCacheMap.getRequest(uuid), LogCacheMap.getResponse(uuid));}return serverWebExchange;}).then();}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}
}
如果也是使用Map存储请求体,注意用不到请求体/响应体后删除他们,避免内存溢出。