文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

Gateway请求应答日志打印

请求应答日志时在日常开发调试问题的重要手段之一,那么如何基于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=[* public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {     private static final Logger logger = LoggerFactory.getLogger(GlobalCacheRequestBodyFilter.class);     private int order;     @Override     public Mono<Void> 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;     } }

编写全局日志拦截器代码


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<Void> 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<Void> 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.mutate().response(decoratedResponse).build());
    }
    @Override
    public int getOrder() {
        return this.order;
    }
    public GlobalLogFilter(int order){
        this.order = order;
    }
}

在代码中配置全局拦截器


@Configuration
public class GatewayConfig {
    
    @Bean
    public GlobalLogFilter globalLogFilter(){
        // 该值越小权重却大,所以应根据具体项目配置。需要尽早的获取到参数,一般会是一个比较小的值
        return new GlobalLogFilter(-20); 
    }
    
    // 其他的路由配置 ... 
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯