請求應答日誌時在日常開發除錯問題的重要手段之一,那麼如何基於Spring Cloud Gateway做呢,請看我上程式碼。
第一步:
建立RecorderServerHttpRequestDecorator,快取請求引數,解決body只能讀一次問題。
public class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private final List<DataBuffer> dataBuffers = new ArrayList<>();
public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
super.getBody().map(dataBuffer -> {
dataBuffers.add(dataBuffer);
return dataBuffer;
}).subscribe();
}
@Override
public Flux<DataBuffer> getBody() {
return copy();
}
private Flux<DataBuffer> copy() {
return Flux.fromIterable(dataBuffers)
.map(buf -> buf.factory().wrap(buf.asByteBuffer()));
}
}
複製程式碼
第二步:
建立訪問日誌全域性過濾器,然後在此過濾器進行日誌構造。
@Slf4j
public class AccessLogGlobalFilter implements GlobalFilter , Ordered {
private static final String REQUEST_PREFIX = "Request Info [ ";
private static final String REQUEST_TAIL = " ]";
private static final String RESPONSE_PREFIX = "Response Info [ ";
private static final String RESPONSE_TAIL = " ]";
private StringBuilder normalMsg = new StringBuilder();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
RecorderServerHttpRequestDecorator requestDecorator = new RecorderServerHttpRequestDecorator(request);
InetSocketAddress address = requestDecorator.getRemoteAddress();
HttpMethod method = requestDecorator.getMethod();
URI url = requestDecorator.getURI();
HttpHeaders headers = requestDecorator.getHeaders();
Flux<DataBuffer> body = requestDecorator.getBody();
//讀取requestBody傳參
AtomicReference<String> requestBody = new AtomicReference<>("");
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
requestBody.set(charBuffer.toString());
});
String requestParams = requestBody.get();
normalMsg.append(REQUEST_PREFIX);
normalMsg.append(";header=").append(headers);
normalMsg.append(";params=").append(requestParams);
normalMsg.append(";address=").append(address.getHostName() + address.getPort());
normalMsg.append(";method=").append(method.name());
normalMsg.append(";url=").append(url.getPath());
normalMsg.append(REQUEST_TAIL);
ServerHttpResponse response = exchange.getResponse();
DataBufferFactory bufferFactory = response.bufferFactory();
normalMsg.append(RESPONSE_PREFIX);
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
// probably should reuse buffers
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
String responseResult = new String(content, Charset.forName("UTF-8"));
normalMsg.append("status=").append(this.getStatusCode());
normalMsg.append(";header=").append(this.getHeaders());
normalMsg.append(";responseResult=").append(responseResult);
normalMsg.append(RESPONSE_TAIL);
log.info(normalMsg.toString());
return bufferFactory.wrap(content);
}));
}
return super.writeWith(body); // if body is not a flux. never got there.
}
};
return chain.filter(exchange.mutate().request(requestDecorator).response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -2;
}
}
複製程式碼
最後結果:
Request Info [ ;header={cache-control=[no-cache], Postman-Token=[790488a5-a284-4a0e-968f-1b588cb26688], Content-Type=[application/json], User-Agent=[PostmanRuntime/3.0.9], Accept=[*/*], Host=[localhost:8084], cookie=[JSESSIONID=E161AC22204E626FBE6E96EE7B62EE70], accept-encoding=[gzip, deflate], content-length=[13], Connection=[keep-alive]};params={"name":"ss"};address=0:0:0:0:0:0:0:159621;method=POST;url=/account/testBody ]Response Info [ ;status=200;header={Content-Type=[text/plain;charset=UTF-8], Content-Length=[41], Date=[Mon, 18 Mar 2019 08:21:57 GMT]};responseResult=account hellowordAccountEntity{name='ss'} ]
複製程式碼
基於以上程式碼即可完成請求應答日誌列印功能。