需要使用请求装饰类和响应装饰类,把请求体和响应体保存一下,再在全局Post过滤器里面获得该请求体。
请求装饰类
java
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
@Override
public 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,所以要释放原始的DataBuffer
DataBufferUtils.release(dataBuffer);
// 返回新的DataBuffer
return bufferFactory.wrap(content);
});
}
}
响应装饰类
java
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;
}
@Override
public 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);
}
}
用到的自定义工具类
java
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");
}
}
全局过滤器类
java
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 {
@Resource
AccessLogService logService;
@Override
public 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();
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
如果也是使用Map存储请求体,注意用不到请求体/响应体后删除他们,避免内存溢出。