SpringCloud Gateway之请求应答日志打印方式

网友投稿 2619 2022-10-23

SpringCloud Gateway之请求应答日志打印方式

SpringCloud Gateway之请求应答日志打印方式

目录Gateway请求应答日志打印第一步第二步Gateway全局请求日志打印把请求体的数据存入exchange编写全局日志-代码在代码中配置全局-

Gateway请求应答日志打印

请求应答日志时在日常开发调试问题的重要手段之一,那么如何基于Spring Cloud Gateway做呢,请看我上代码。

第一步

创建RecorderServerHttpRequestDecorator,缓存请求参数,解决body只能读一次问题。

public class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator {

private final List dataBuffers = new ArrayList<>();

public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {

super(delegate);

super.getBody().map(dataBuffer -> {

dataBuffers.add(dataBuffer);

return dataBuffer;

}).subscribe();

}

@Override

public Flux getBody() {

return copy();

}

private Flux 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 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 body = requestDecorator.getBody();

//读取requestBody传参

AtomicReference 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);

ServerHttpResponseDeckzpXeNPorator decoratedResponse = new ServerHttpResponseDecorator(response) {

@Override

public Mono 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'} ]

以上代码即可完成请求应答日志打印功能。

Gateway全局请求日志打印

实现GlobalFilter则所有该自定义Filter会对所有的路由生效。

把请求体的数据存入exchange

便于打印日志时获取

package com.qykj.gateway.filter;

import com.qykj.gateway.ConstantFilter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;

import org.springframework.cloud.gateway.filter.GlobalFilter;

import org.springframework.core.Ordered;

import org.springframework.core.io.buffer.DataBufferUtils;

import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

/**

* @calssName AppCacheRequestBodyFilter

* @Description 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中

* @Author jiangshaoneng

* @DATE 2020/9/27 14:42

*/

public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {

private static final Logger logger = LoggerFactory.getLogger(GlobalCacheRequestBodyFilter.class);

private int order;

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

//logger.info("GlobalCacheRequestBodyFilter ...");

// 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中

Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);

// 如果已经缓存过,略过

if (cachedRequestBodyObject != null) {

return chain.filter(exchange);

}

// 如果没有缓存过,获取字节数组存入 exchange 的自定义属性中

return DataBufferUtils.join(exchange.getRequest().getBody())

.map(dataBuffer -> {

byte[] bytes = new byte[dataBuffer.readableByteCount()];

dataBuffer.read(bytes);

DataBufferUtils.release(dataBuffer);

return bytes;

}).defaultIfEmpty(new byte[0])

.doOnNext(bytes -> exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, bytes))

.then(chain.filter(exchange));

}

@Override

public int getOrder() {

return this.order;

}

public GlobalCacheRequestBodyFilter(int order){

this.order = order;

}

}

编写全局日志-代码

/**

* @calssName LogFilter

* @Description 全局日志打印,请求日志以及返回日志,并在返回结果日志中添加请求时间

* @Author jiangshaoneng

* @DATE 2020/9/25 14:54

*/

public class GlobalLogFilter implements GlobalFilter, Ordered {

private static final Logger logger = LoggerFactory.getLogger(GlobalLogFilter.class);

private int order;

private static final String REQUEST_PREFIX = "\n--------------------------------- Request Info -----------------------------";

private static final String REQUEST_TAIL = "\n-----------------------------------------------------------------------------";

private static final String RESPONSE_PREFIX = "\n--------------------------------- Response Info -----------------------------";

private static final String RESPONSE_TAIL = "\n-------------------------------------------------------------------------->>>";

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

long start = DateUtil.getCurrentTime();

StringBuilder reqMsg = new StringBuilder();

StringBuilder resMsg = new StringBuilder();

// 获取请求信息

ServerHttpRequest request = exchange.getRequest();

InetSocketAddress address = request.getRemoteAddress();

String method = request.getMethodValue();

URI uri = request.getURI();

HttpHeaders headers = request.getHeaders();

// 获取请求body

Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);

byte[] body = (byte[]) cachedRequestBodyObject;

String params = new String(body);

// 获取请求query

Map queryMap = request.getQueryParams();

String query = JSON.toJSONString(queryMap);

// 拼接请求日志

reqMsg.append(REQUEST_PREFIX);

reqMsg.append("\n header=").append(headers);

reqMsg.append("\n query=").append(query);

reqMsg.append("\n params=").append(params);

reqMsg.append("\n address=").append(address.getHostName()).append(address.getPort());

reqMsg.append("\n method=").append(method);

reqMsg.append("\n url=").append(uri.getPath());

reqMsg.append(REQUEST_TAIL);

logger.info(reqMsg.toString()); // 打印入参日志

ServerHttpResponse response = exchange.getResponse();

DataBufferFactory bufferFactory = response.bufferFactory();

resMsg.append(RESPONSE_PREFIX);

ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(response) {

@Override

public Mono writeWith(Publisher extends DataBuffer> body) {

if (body instanceof Flux) {

Flux extends DataBuffer> fluxBody = (Flux extends DataBuffer>) body;

return super.writeWith(fluxBody.map(dataBuffer -> {

byte[] content = new byte[dataBuffer.readableByteCount()];

dataBuffer.read(content);

String responseResult = new String(content, Charset.forName("UTF-8"));

resMsg.append("\n status=").append(this.getStatusCode());

resMsg.append("\n header=").append(this.getHeaders());

resMsg.append("\n responseResult=").append(responseResult);

resMsg.append(RESPONSE_TAIL);

// 计算请求时间

long end = DateUtil.getCurrentTime();

long time = end - start;

resMsg.append("耗时ms:").append(time);

logger.info(resMsg.toString()); // 打印结果日志

return bufferFactory.wrap(content);

}));

}

return super.writeWith(body);

}

};

return chain.filter(exchange.muthttp://ate().response(decoratedResponse).build());

}

@Override

public int getOrder() {

return this.order;

}

public GlobalLogFilter(int order){

this.order = order;

}

}

在代码中配置全局-

/**

* @calssName GatewayConfig

* @Description 网关配置

* @Author jiangshaoneng

* @DATE 2020/9/25 14:26

*/

@Configuration

public class GatewayConfig {

/**

* 全局过滤器:请求日志打印

*/

@Bean

public GlobalLogFilter globalLogFilter(){

// 该值越小权重却大,所以应根据具体项目配置。需要尽早的获取到参数,一般会是一个比较小的值

return new GlobalLogFilter(-20);

}

// 其他的路由配置 ...

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:突破神奇的Cloudflare防火墙
下一篇:elton- 高性能简单的 Go Web 框架
相关文章

 发表评论

暂时没有评论,来抢沙发吧~