Gateway网关打印请求日志

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Gateway网关打印请求日志

一、请求日志输出

package com.tencentcloudapi.gateway.filter;
import com.tencentcloudapi.gateway.api.utils.UuidUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.slf4j.MDC;
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.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.nio.charset.StandardCharsets;
/**
 * @author Miller.Lai
 * @description: 请求日志输出过滤器
 * @date 2024-01-31 14:26:33
 */
@Component
@Slf4j
public class WrapperRequestGlobalFilter implements GlobalFilter, Ordered {
    /**
     * 优先级最高
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         // 在 ServerWebExchange 中设置变量
        exchange.getAttributes().put("requestId", UuidUtil.getShortUuid());
        // 添加线程日志号
        MDC.put("UUID", Thread.currentThread().getName());
        ServerHttpRequest request = exchange.getRequest();
        URI URIPath = request.getURI();
        String path = request.getPath().value();
        String method = request.getMethod().toString();
        HttpHeaders header = request.getHeaders();
        log.info("");
        log.info("{} --------------------- 请求开始 ------------------------",exchange.getAttributes().get("requestId"));
        log.info("{} 原始请求信息:URI = {}, path = {},method = {},header = {}。",exchange.getAttributes().get("requestId"), URIPath, path, method, header);
        if ("POST".equals(method)) {
            return DataBufferUtils.join(exchange.getRequest().getBody())
                    .flatMap(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        String bodyString = new String(bytes, StandardCharsets.UTF_8);
                        bodyString = bodyString.replaceAll("\"ImageBase64\":\"[^\"]*\",", "");
                        log.info("{} 请求参数,不包含ImageBase64:" + bodyString,exchange.getAttributes().get("requestId"));
                        exchange.getAttributes().put("POST_BODY", bodyString);
                        DataBufferUtils.release(dataBuffer);
                        Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                            DataBuffer buffer = exchange.getResponse().bufferFactory()
                                    .wrap(bytes);
                            return Mono.just(buffer);
                        });
                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                                exchange.getRequest()) {
                            @NotNull
                            @Override
                            public Flux<DataBuffer> getBody() {
                                return cachedFlux;
                            }
                        };
                        return chain.filter(exchange.mutate().request(mutatedRequest)
                                .build()).doFinally(signalType -> {
                            // 请求处理完毕后清理变量
                            exchange.getAttributes().remove("requestId");
                            exchange.getAttributes().remove("hasAcquired");
                        });
                    });
        } else if ("GET".equals(method)) {
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            log.info("{} 请求参数:" + queryParams,exchange.getAttributes().get("requestId"));
            return chain.filter(exchange);
        }
        return chain.filter(exchange);
    }
}

二、响应日志输出

package com.tencentcloudapi.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
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.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
 * @author Miller.Lai
 * @description: 响应日志输出过滤器
 * @date 2024-01-31 14:26:55
 */
@Component
@Slf4j
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public int getOrder() {
        //-1 is response write filter, must be called before that
        return -2;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取response的 返回数据
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        StringBuffer responseBuffer =new StringBuffer();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @NotNull
            @Override
            public Mono<Void> writeWith(@NotNull Publisher<? extends DataBuffer> body) {
                if ( body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        //释放掉内存
                        DataBufferUtils.release(dataBuffer);
                        //responseData就是下游系统返回的内容,可以查看修改
                        String responseData = new String(content, StandardCharsets.UTF_8);
                        responseBuffer.append(responseData);
                        if(responseBuffer.indexOf("RequestId") != -1  && responseBuffer.substring(responseBuffer.indexOf("RequestId")).length()>=48){
                            String responseStr = responseBuffer.toString();
                            String requestId = responseStr.substring(responseStr.indexOf("RequestId")+12,responseStr.indexOf("RequestId")+48);
                            responseBuffer.setLength(0);
                            log.info("{} 响应结果,code:{},RequestId:{}",exchange.getAttributes().get("requestId"),getStatusCode(),requestId);
                            log.info("{} --------------------- 请求结束 ------------------------",exchange.getAttributes().get("requestId"));
                            log.info("");
                        }
                        byte[] uppedContent = new String(content, StandardCharsets.UTF_8).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
    public static void main(String[] args) {
        String a = "\"RequestId\":\"1f123f96-56c7-4be4-9b4b-ac844de6f10b\"";
        String b = a.substring(a.indexOf("RequestId")+12,a.indexOf("RequestId")+48);
        System.out.println(a.substring(a.indexOf("RequestId")));
        System.out.println(b);
    }
}


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7月前
|
负载均衡 Java 应用服务中间件
Gateway服务网关
Gateway服务网关
225 1
Gateway服务网关
|
7月前
|
负载均衡 Java API
项目中用的网关Gateway及SpringCloud
Spring Cloud Gateway 是一个功能强大、灵活易用的API网关解决方案。通过配置路由、过滤器、熔断器和限流等功能,可以有效地管理和保护微服务。本文详细介绍了Spring Cloud Gateway的基本概念、配置方法和实际应用,希望能帮助开发者更好地理解和使用这一工具。通过合理使用Spring Cloud Gateway,可以显著提升微服务架构的健壮性和可维护性。
251 0
|
9月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
424 5
|
8月前
|
缓存 网络协议 API
【Azure 环境】请求经过应用程序网关,当响应内容大时遇见504超时报错
应用程序网关的响应缓冲区可以收集后端服务器发送的全部或部分响应数据包,然后再将它们发送给客户端。 默认在应用程序网关上启用响应缓冲,这对于适应缓慢的客户端很有用。
|
10月前
|
Kubernetes 数据安全/隐私保护 容器
【Azure APIM】APIM Self-Hosted网关中,添加网关日志以记录请求头信息(Request Header / Response Header)
【Azure APIM】APIM Self-Hosted网关中,添加网关日志以记录请求头信息(Request Header / Response Header)
105 3
|
10月前
|
Java API 微服务
服务网关Gateway
该博客文章详细介绍了Spring Cloud Gateway的使用方法和概念。文章首先阐述了API网关在微服务架构中的重要性,解释了客户端直接与微服务通信可能带来的问题。接着,文章通过具体的示例代码,展示了如何在Spring Cloud Gateway中添加依赖、编写路由规则,并对路由规则中的基本概念如Route、Predicate和Filter进行了详细解释。最后,文章还提供了路由规则的测试方法。
服务网关Gateway
|
10月前
|
存储 Kubernetes API
【APIM】Azure API Management Self-Host Gateway是否可以把请求的日志发送到Application Insights呢?让它和使用Azure上托管的 Gateway一样呢?
【APIM】Azure API Management Self-Host Gateway是否可以把请求的日志发送到Application Insights呢?让它和使用Azure上托管的 Gateway一样呢?
|
10月前
|
安全 API
【Azure API 管理】APIM Self-Host Gateway 自建本地环境中的网关数量超过10个且它们的出口IP为同一个时出现的429错误
【Azure API 管理】APIM Self-Host Gateway 自建本地环境中的网关数量超过10个且它们的出口IP为同一个时出现的429错误
132 0
|
10月前
|
存储 容器
【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
【Azure 事件中心】为应用程序网关(Application Gateway with WAF) 配置诊断日志,发送到事件中心
129 0
|
6月前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
363 69
利用Spring Cloud Gateway Predicate优化微服务路由策略