从零搭建SpringCloudGateway网关(二)——打印请求响应日志

更新时间:2023-05-12 02:36:01 阅读: 评论:0

从零搭建SpringCloudGateway⽹关(⼆)——打印请求响应⽇志
作为⽹关,⽇志记录是必不可少的功能,可以在⽹关出增加requestId来查询整个请求链的调⽤执⾏情况等等。
打印请求⽇志
打印请求⽇志最重要的就是打印请求参数这些东西,不过RequestBody通常情况下在被读取⼀次之后就会失效,这样的话,下游的服务就不能正常获取到请求参数了。所以我们需要重写下请求体。
具体⽅法呢有很多,这⾥说⼀下我⽤的两种:
第⼀种
代码如下:
package com.lifengdi.gateway.filter;
import com.stant.HeaderConstant;
import com.stant.OrderedConstant;
import com.lifengdi.gateway.log.Log;
import com.lifengdi.gateway.log.LogHelper;
import com.lifengdi.gateway.utils.GenerateIdUtils;
import com.lifengdi.gateway.utils.IpUtils;
buffer.UnpooledByteBufAllocator;
slf4j.Slf4j;
import s.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import Ordered;
import io.buffer.DataBuffer;
import io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.active.ServerHttpRequest;
import org.springframework.active.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.active.function.rver.HandlerStrategies;
import org.active.function.rver.ServerRequest;
import org.springframework.web.rver.ServerWebExchange;
publisher.Flux;
publisher.Mono;
URI;
import java.util.Objects;
import urrent.atomic.AtomicBoolean;
import urrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
* 请求⽇志打印
*/
@Component
@Slf4j
public class RequestLogFilter implements GlobalFilter, Ordered {
@Override
public int getOrder(){
return OrderedConstant.REQUEST_FILTER;
}
@Override
public Mono<Void>filter(ServerWebExchange exchange, GatewayFilterChain chain){
StopWatch stopWatch =new StopWatch();
stopWatch.start();
stopWatch.start();
long startTime = System.currentTimeMillis();
try{
ServerHttpRequest request = Request();
// 设置X-Request-Id
AtomicReference<String> requestId =new AtomicReference<>(questIdWithUUID());
Consumer<HttpHeaders> httpHeadersConsumer = httpHeaders ->{
String headerRequestId = Headers().getFirst(HeaderConstant.REQUEST_ID);
if(StringUtils.isBlank(headerRequestId)){
httpHeaders.t(HeaderConstant.REQUEST_ID, ());
}el{
requestId.t(headerRequestId);
}
httpHeaders.t(HeaderConstant.START_TIME_KEY, String.valueOf(startTime));
};
ServerRequest rverRequest = ate(exchange,
HandlerStrategies.withDefaults().messageReaders());
URI requestUri = URI();
String uriQuery = Query();
String url = Path()+(StringUtils.isNotBlank(uriQuery)?"?"+ uriQuery :"");
HttpHeaders headers = Headers();
MediaType mediaType = ContentType();
String method = MethodValue().toUpperCa();
// 原始请求体
final AtomicReference<String> requestBody =new AtomicReference<>();
final AtomicBoolean newBody =new AtomicBoolean(fal);
Null(mediaType)&& LogHelper.isUploadFile(mediaType)){
requestBody.t("上传⽂件");
}el{
if(method.equals("GET")){
if(StringUtils.isNotBlank(uriQuery)){
requestBody.t(uriQuery);
}
}el{
newBody.t(true);
}
}
final Log logDTO =new Log();
logDTO.tLevel(Log.LEVEL.INFO);
logDTO.tRequestUrl(url);
logDTO.());
logDTO.tRequestMethod(method);
logDTO.());
logDTO.ClientIp(request));
ServerHttpRequest rverHttpRequest = Request().mutate().headers(httpHeadersConsumer).build();
ServerWebExchange build = exchange.mutate().request(rverHttpRequest).build();
Session().flatMap(webSession ->{
logDTO.Id());
()&& ContentLength()>0){
Mono<String> bodyToMono = rverRequest.bodyToMono(String.class);
return bodyToMono.flatMap(reqBody ->{
logDTO.tRequestBody(reqBody);
// 重写原始请求
ServerHttpRequestDecorator requestDecorator =new Request()){ @Override
public Flux<DataBuffer>getBody(){
NettyDataBufferFactory nettyDataBufferFactory =new NettyDataBufferFactory(new UnpooledByteBufAllocator(fal));                                DataBuffer bodyDataBuffer = nettyDataBuffer
Factory.Bytes());
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate()
.request(requestDecorator)
.build()).then(LogHelper.doRecord(logDTO));
.build()).then(LogHelper.doRecord(logDTO));
});
}el{
return chain.filter(exchange).then(LogHelper.doRecord(logDTO));
}
});
}catch(Exception e){
<("请求⽇志打印出现异常", e);
return chain.filter(exchange);
}
}
}
上⾯的核⼼代码是:
// 重写原始请求
ServerHttpRequestDecorator requestDecorator =new Request()){
@Override
public Flux<DataBuffer>getBody(){
NettyDataBufferFactory nettyDataBufferFactory =new NettyDataBufferFactory(new UnpooledByteBufAllocator(fal));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.Bytes());
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate()
.request(requestDecorator)
.build()).then(LogHelper.doRecord(logDTO));
如果不需要对ssion进⾏操作,可以直接调⽤这块就⾏。
关于请求时间,我这⾥采⽤的是将时间戳放进请求头中,等到打印⽇志的时候再从请求头中读取然后计算出时间。否则如果单独在某个filter中计算请求时间,会造成时间不太准确。当然这样时间也不是很准确,毕竟还有Spring本⾝的filter等业务逻辑,不过时间相差不是很⼤,⼤概⼗⼏毫秒的样⼦。
第⼆种
第⼆种就是⾃⼰缓存下请求体,读取的时候读取缓存内容。
代码如下:
package com.lifengdi.gateway.log;
import com.stant.HeaderConstant;
import com.lifengdi.gateway.utils.IpUtils;
buffer.UnpooledByteBufAllocator;
slf4j.Slf4j;
import s.io.IOUtils;
import s.lang.StringUtils;
import io.buffer.DataBuffer;
import io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.active.ServerHttpRequest;
import org.springframework.active.ServerHttpRequestDecorator;
publisher.Flux;
scheduler.Schedulers;
import java.io.IOException;
import java.io.InputStream;
URI;
import java.util.Objects;
/**
/**
* 对ServerHttpRequest进⾏⼆次封装,解决requestBody只能读取⼀次的问题
* @author: Li Fengdi
* @date: 2020-03-17 18:02
*/
@Slf4j
public class CacheServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private DataBuffer bodyDataBuffer;
private int getBufferTime =0;
private byte[] bytes;
public CacheServerHttpRequestDecorator(ServerHttpRequest delegate){
super(delegate);
}
@Override
public Flux<DataBuffer>getBody(){
if(getBufferTime ==0){
getBufferTime++;
Flux<DataBuffer> flux =Body();
return flux.publishOn(Schedulers.single())
.
map(this::cache)
.doOnComplete(()->trace(getDelegate()));
}el{
return Flux.just(getBodyMore());
}
}
private DataBuffer getBodyMore(){
NettyDataBufferFactory nettyDataBufferFactory =new NettyDataBufferFactory(new UnpooledByteBufAllocator(fal));        bodyDataBuffer = nettyDataBufferFactory.wrap(bytes);
return bodyDataBuffer;
}
private DataBuffer cache(DataBuffer buffer){
try{
InputStream dataBuffer = buffer.asInputStream();
bytes = ByteArray(dataBuffer);
NettyDataBufferFactory nettyDataBufferFactory =new NettyDataBufferFactory(new UnpooledByteBufAllocator(fal));            bodyDataBuffer = nettyDataBufferFactory.wrap(bytes);
return bodyDataBuffer;
}catch(IOException e){
e.printStackTrace();
}
return null;
}
private void trace(ServerHttpRequest request){
URI requestUri = URI();
String uriQuery = Query();
String url = Path()+(StringUtils.isNotBlank(uriQuery)?"?"+ uriQuery :"");
HttpHeaders headers = Headers();
MediaType mediaType = ContentType();
String schema = Scheme();
String method = MethodValue().toUpperCa();
if((!"http".equals(schema)&&!"https".equals(schema))){
return;
}
String reqBody = null;
Null(mediaType)&& LogHelper.isUploadFile(mediaType)){
reqBody ="上传⽂件";
}el{
if(method.equals("GET")){
if(StringUtils.isNotBlank(uriQuery)){
if(StringUtils.isNotBlank(uriQuery)){
reqBody = uriQuery;
}
}el ContentLength()>0){
reqBody = adRequestBody(request);
}
}
final Log logDTO =new Log();
logDTO.tLevel(Log.LEVEL.INFO);
logDTO.tRequestUrl(url);
logDTO.tRequestBody(reqBody);
logDTO.tRequestMethod(method);
logDTO.First(HeaderConstant.REQUEST_ID));
logDTO.ClientIp(request));
log.JsonString(logDTO));
}
}
filter这⾥就简单写下:
package com.lifengdi.gateway.filter;
import com.stant.OrderedConstant;
import com.lifengdi.gateway.log.CacheServerHttpRequestDecorator;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import Ordered;
import org.springframework.web.rver.ServerWebExchange;
publisher.Mono;
/**
* @author: Li Fengdi
* @date: 2020-03-17 18:17
*/
//@Component
public class LogFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void>filter(ServerWebExchange exchange, GatewayFilterChain chain){
CacheServerHttpRequestDecorator cacheServerHttpRequestDecorator =new Request());
return chain.filter(exchange.mutate().request(cacheServerHttpRequestDecorator).build());
}
@Override
public int getOrder(){
return OrderedConstant.LOGGING_FILTER;
}
}
⼯具类也贴下:
package com.lifengdi.gateway.log;
import com.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stant.HeaderConstant;
slf4j.Slf4j;
import s.lang.StringUtils;
import io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.active.ServerHttpRequest;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
publisher.Mono;

本文发布于:2023-05-12 02:36:01,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/885938.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:请求   读取   时间   打印   时候   记录
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图