Spring Cloud Gateway日志级别链路追踪设计

这篇具有很好参考价值的文章主要介绍了Spring Cloud Gateway日志级别链路追踪设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

日志格式化目的

​ 为了配合日志分析系统ELK(ElasticSearch,Logstash,Kibana)方便解析日志文件,需要对日志文件的输出格式进行JSON格式化,我这里使用的日志工具是logback(幸运的躲过了log4j的漏洞)+logstash-encoder包进行的封装的一个日志插件,该插件实现了日志JSON格式化,适配了多种中间件的链路追踪,支持了中间件,RPC的远程调用的耗时计算等,并且完美支持spring xml接入和spring boot接入两种方式。总的来说我这里接入这个日志插件有三方面原因,1.日志格式化、2.日志链路追踪、3.中间过程耗时分析。

​ 日志插件的快速接入方式这里不做过多介绍了,可以参考我的这个插件的开源项目,项目里提供了插件接入文档已经使用示例,项目地址是,也可以参考我的博客文章。

Spring Cloud Gateway日志级别链路追踪设计

log-helper插件开始并不支持Spring Cloud Gateway的链路追踪及转发下游接口的耗时统计,所以在Spring Cloud Alibaba开发脚手架搭建的过程中意识到了这一点,并且对其进行了适配,下面就简单说一下适配日志链路追踪的方式。

log-helper插件是基于MDC实现的链路追踪,各个中间件适配也是基于中间件的FilterInterceptor实现的,所以Spring Cloud Gateway也是沿用这个思路。

Spring Cloud Gateway提供了过滤器功能,这里简单介绍一下, Spring Cloud Gateway的过滤器, Spring Cloud Gateway提供了两个过滤器,一个全局过滤器,一个局部过滤器,下面简单介绍一下他们之间的区别。

  • 全局过滤器:GlobalFilter,这是一个接口,实现了该接口并实现filter方法,所有的请求都会经过该过滤器。

  • 局部过滤器:AbstractGatewayFilterFactory,它是一个抽象类,继承该抽象类,并重写apply方法后需要在网关的配置文件中配置过滤器,如果不配置是不会流经该过滤器的,配置项是spring.cloud.gateway.routes.filters[].name中配置,具体配置示例如下:

    spring:
      
      cloud:
        gateway:
          routes:
            - id: account-svc
              uri: lb://account-svc
              predicates:
                - Path=/gateway/account/**
              filters:
                - StripPrefix=1
                # url黑名单 这是一个局部过滤器
                - name: BlackListUrlFilter
                  args: 
                    blackListUrl:
                      - /account/list
    
    

    BlackListUrlFilter对应的就是继承了AbstractGatewayFilterFactory的过滤器类,该实现要是Spring管理的。

​ 通过上面对Spring Cloud Gateway过滤器技术的介绍,下面我们来实现我们日志插件的过滤器,这里选用的就应该是全局过滤器,因为对于网关来说,所有的请求都应该经过该过滤器,并且这里需要两个过滤器,一个是链路追踪的过滤器,一个是转发接口响应时间计算的过滤器,下面具体介绍两个过滤器实现。

链路追踪过滤器-TracerFilter

​ 实现该过滤器后,就是要从Http请求头中获取链路信息,这其实也是一种规范,如果整个系统都要实现链路追踪,那么我们就一定要有链路追踪数据的规范,包括不限于 数据传输 规范,数据生成规范等,更高级的链路追踪规范可以参考OpenTracing协议,该协议是APM软件的协议,我这里也对该协议进行了一些参考,并实现了自己的日志级别的Tracer

​ 简单说链路追踪过滤器的逻辑就是从Http请求头中获取链路数据,如果有链路数据就直接用获取到的数据组装Tracer数据,如果没有链路数据那么就直接生成Tracer数据,并将Tracer数据传递到转发的接口上,传递也是将Tracer数据通过Http请求头进行传递。具体实现代码如下:

AbstractInterceptorlog-helper插件的抽象类,作用是从MDC中获取链路数据和调用其他链路前后做一些时间计算和日志打标签的功能,Tracerlog-helper提供的OpenTracing的参考实现。

​ 值得注意的是这里实现了Ordered接口并且实现了getOrder接口,该接口是Spring提供的顺序管理的接口,目的是告诉整个过滤器调用链,该过滤器的优先级是最高的,也就是说要最先执行(Ordered.HIGHEST_PRECEDENCE)。

filter方法在处理完链路数据后调用了executeBefore(SCG_INVOKE_START + request.getURI().getPath());方法,该方法是给日志打了一个网关开始处理的标签,AbstractInterceptor的代码可以参考log-helper

@Slf4j
public class TracerFilter extends AbstractInterceptor implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        Mono<Void> voidMono = chain.filter(exchange.mutate()
                .request(builder -> builder.headers(httpHeaders -> {
                    List<String> traceList = httpHeaders.get(Tracer.TRACE_ID);
                    String traceId = null;
                    if (traceList != null && traceList.size() > 0) {
                        traceId = traceList.get(0);
                    }
                    List<String> spanList = httpHeaders.get(Tracer.SPAN_ID);
                    String spanId = null;
                    if (spanList != null && spanList.size() > 0) {
                        spanId = spanList.get(0);
                    }
                    List<String> parentList = httpHeaders.get(Tracer.PARENT_ID);
                    String parentId = null;
                    if (parentList != null && parentList.size() > 0) {
                        parentId = parentList.get(0);
                    }
                    Tracer.trace(traceId, spanId, parentId);
                    httpHeaders.set(Tracer.TRACE_ID, traceId());
                    httpHeaders.set(Tracer.PARENT_ID, parentId());
                    httpHeaders.set(Tracer.SPAN_ID, spanId());
                })).build());
        executeBefore(SCG_INVOKE_START + request.getURI().getPath());
        return voidMono;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

转发接口响应耗时计算过滤器-RtFilter

​ 该接口的目的就是计算出网关处理完一个请求的耗时,具体代码如下:

@Slf4j
public class RtFilter extends AbstractInterceptor implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        return chain.filter(exchange).transformDeferred(request -> {
            final long start = System.currentTimeMillis();
            return request.doFinally(s -> {
               MDC.put(START_TIME, String.valueOf(start));
               executeAfter(SCG_INVOKE_END);
            });
        });
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 1;
    }
}

Spring Boot Autoconfigure实现

​ 两个过滤器实现完,我们要将其在项目启动时加载到Spring中,下面就是通过Spring Boot自动装配实现;关于自动装配知识这里不做过多介绍。

  • spring.factories

​ 在resources/META-INF/spring.factories中配置如下内容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.redick.starter.configure.ScgTraceAutoConfiguration
  • Config类编写
@Configuration
public class ScgTraceAutoConfiguration {
  
    @Bean
    public TracerFilter tracerFilter() {
        return new TracerFilter();
    }

    @Bean
    public RtFilter rtFilter() {
        return new RtFilter();
    }
}

Spring Cloud Gateway日志链路追踪发测试

  • 准备两个服务
  1. ruuby-gateway:网关
  2. ruuby-account-svc:库存服务
  • 验证

​ 启动两个服务并通过网关请求接口curl -X GET http://127.0.0.1:8081/gateway/account/list,日志结果如下:

ruuby-gateway日志:

​ 网关层的日志记录了链路追踪数据traceId,parentId,spanId和日志标签trace_tag,并且在第三条日志的duration中记录了此次请求处理耗时490ms,可以看到通过这些链路数据可以在ELK平台上就可以进行日志分析和问题排查了。

{"@timestamp":"2023-05-22T19:26:08.742+08:00","@version":"0.0.1","message":"scg_invoke_start/gateway/account/list","logger_name":"com.redick.support.AbstractInterceptor","thread_name":"reactor-http-nio-2","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"1","parentId":"0","trace_tag":"scg_invoke_start/gateway/account/list"}
{"@timestamp":"2023-05-22T19:26:08.829+08:00","@version":"0.0.1","message":"LoadBalancerCacheManager not available, returning delegate without caching.","logger_name":"org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplierBuilder","thread_name":"reactor-http-nio-2","level":"WARN","level_value":30000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"1","parentId":"0"}
{"@timestamp":"2023-05-22T19:26:09.364+08:00","@version":"0.0.1","message":"scg_invoke_end","logger_name":"com.redick.support.AbstractInterceptor","thread_name":"reactor-http-nio-4","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"1","parentId":"0","trace_tag":"scg_invoke_end","duration":490}

ruuby-account-svc日志:

​ ruuby-account-svc接口的日志如下,通过网关的链路数据traceId就能够查到ruuby-account-svc服务的日志,致此就实现了日志层面的链路追踪。

{"@timestamp":"2023-05-22T19:26:09.149+08:00","@version":"0.0.1","message":"开始处理","logger_name":"io.redick.cloud.account.controller.AccountController","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","log_pos":"开始处理","data":[]}
{"@timestamp":"2023-05-22T19:26:09.205+08:00","@version":"0.0.1","message":"slave2数据库","logger_name":"io.redick.cloud.datasource.context.DBContextHolder","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","log_pos":"过程"}
{"@timestamp":"2023-05-22T19:26:09.247+08:00","@version":"0.0.1","message":"开始执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","trace_tag":"sql_exec_before"}
{"@timestamp":"2023-05-22T19:26:09.250+08:00","@version":"0.0.1","message":"结束执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","trace_tag":"sql_exec_after","duration":3}
{"@timestamp":"2023-05-22T19:26:09.277+08:00","@version":"0.0.1","message":"开始执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","trace_tag":"sql_exec_before"}
{"@timestamp":"2023-05-22T19:26:09.285+08:00","@version":"0.0.1","message":"结束执行sql","logger_name":"com.redick.support.mysql.Mysql5StatementInterceptor","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","trace_tag":"sql_exec_after","duration":8}
{"@timestamp":"2023-05-22T19:26:09.322+08:00","@version":"0.0.1","message":"处理完毕","logger_name":"io.redick.cloud.account.controller.AccountController","thread_name":"http-nio-8088-exec-1","level":"INFO","level_value":20000,"traceId":"44a806f6-ea41-4e3b-85a5-477052d77474","spanId":"2","request_type":"account#list","parentId":"1","log_pos":"处理完毕","data":{"msg":null,"serialVersionUID":1,"SUCCESS":200,"code":200,"data":[{"pageIndex":0,"pageSize":10,"orderByColumn":"","asc":"asc","productId":"123","totalCount":200,"productName":"华为P100","beginTime":null,"endTime":null,"productDesc":"华为手机牛逼"},{"pageIndex":0,"pageSize":10,"orderByColumn":"","asc":"asc","productId":"200","totalCount":100,"productName":"华为Mate100","beginTime":null,"endTime":null,"productDesc":"华为手机真牛逼"}],"FAIL":500},"duration":172,"trace_tag":"endpoint_done","result":"成功"}

源码

源码参考GitHub,如果对您有帮助麻烦点个赞支持下,谢谢!文章来源地址https://www.toymoban.com/news/detail-507159.html

到了这里,关于Spring Cloud Gateway日志级别链路追踪设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud【分组消费、为什么需要链路追踪 、Spring Cloud Sleuth是什么、微服务集成Sleuth实现链路打标】(十二)

      目录 消息驱动_分组消费 分布式请求链路追踪_为什么需要链路追踪 

    2024年02月14日
    浏览(53)
  • Spring Cloud Alibaba全家桶——微服务链路追踪SkyWalking

    本文小新为大家带来 微服务链路追踪SkyWalking 相关知识,具体内容包括 SkyWalking简介 , SkyWalking环境搭建部署 , SkyWalking接入微服务 , SkyWalking持久化跟踪数据 , 自定义SkyWalking链路追踪 , SkyWalking集成日志框架 , SkyWalking告警功能 , SkyWalking高可用 , SkyWalking UI介绍 等进行

    2023年04月08日
    浏览(53)
  • spring cloud gateway网关和链路监控

    文章目录 目录 文章目录 前言 一、网关 1.1 gateway介绍 1.2 如何使用gateway  1.3 网关优化 1.4自定义断言和过滤器 1.4.1 自定义断言 二、Sleuth--链路追踪 2.1 链路追踪介绍 2.2 Sleuth介绍 2.3 使用 2.4 Zipkin的集成  2.5 使用可视化zipkin来监控微服务 总结 所谓的API网关,就是指系统的 统

    2024年02月02日
    浏览(38)
  • SkyWalking链路追踪-搭建-spring-boot-cloud-单机环境 之《10 分钟快速搭建 SkyWalking 服务》

    首先了解一下单机环境 第一步,搭建一个 Elasticsearch 服务。 第二步,下载 SkyWalking 软件包。 第三步,搭建一个 SkyWalking OAP 服务。 第四步,启动一个 Spring Boot 应用,并配置 SkyWalking Agent。 第五步,搭建一个 SkyWalking UI 服务。 准备工作,准备一个docker网络组,网络组的名字为

    2024年02月15日
    浏览(59)
  • 基于Spring-cloud-gateway实现全局日志记录

    最近项目在线上运行出现了一些难以复现的bug需要定位相应api的日志,通过nginx提供的api请求日志难以实现,于是在gateway通过全局过滤器记录api请求日志。 接受到用户请求后,经过全局过滤器,检验是否开启相应的日志配置及相应的黑白名单配置 在gateway前置处理如记录当前

    2024年02月11日
    浏览(61)
  • Spring Cloud Alibaba 最新版本(基于Spring Boot 3.1.0)整合完整使用及与各中间件集成 Sleuth+Zipkin集成分布式链路追踪

    目录 前言 源码地址 官方中文文档 使用版本 spring Spring Boot 3.1.0 中间件 使用到的组件与功能 环境安装 虚拟机 nexus nacos 集成过程 工程搭建 父工程搭建 子工程 服务集成 nacos集成 配置文件 服务注册与发现-discovery 服务注册 启动 服务发现 测试 配置管理-config 新增配置  测试

    2024年02月12日
    浏览(49)
  • java+springboot 做日志链路追踪

    日志链路追踪(Log Path Tracing)是Spring Boot项目的一项关键功能,它通过将日志消息的源头与其对应的请求或响应路径相关联,实现对日志数据的可视化跟踪。 随着项目规模的扩大和复杂性的增加,追踪和管理日志数据变得越来越重要。通过实现日志链路追踪,我们可以更好地

    2024年02月05日
    浏览(38)
  • 分布式链路追踪专栏,分布式链路追踪:Skywalking集群管理设计

    SkyWalking 是一个开源 APM 系统,包括针对 Cloud Native 体系结构中的分布式系统的监视,跟踪,诊断功能。核心功能如下: 服务、服务实例、端点指标分析; 根本原因分析,在运行时分析代码; 服务拓扑图分析; 服务,服务实例和端点依赖性分析; 检测到慢速服务和端点; 性

    2024年02月01日
    浏览(78)
  • spring cloud gateway中出现503 spring cloud gateway中出现503

    当搭建网关模块的时候出现503的错误的最大的可能就是没有设置负载均衡的依赖包  原先搭建的时候采用的是下面的方式进行设置的 上面的这种方式可以直接进行注册和发现,但是要求必须导入下面的依赖 希望简单的随笔能够帮助你!

    2024年02月11日
    浏览(52)
  • Spring Boot日志基础使用 设置日志级别

    然后 我们来说日志 日志在实际开发中还是非常重要的 即可记录项目状态和一些特殊情况发生 因为 我们这里不是将项目 所以 讲的也不会特别深 基本还是将Spring Boot的日志设置或控制这一类的东西 相对业务的领域我们就不涉及了 日志 log 初期最明显的作用在于 开发中 你可以

    2024年02月10日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包