3. 分布式链路追踪的链路日志设计

前言

分布式链路追踪的客户端实现中,我们会通过各种手段和规则得到一个又一个的Span ,得到这些Span 后,需要在分布式链路追踪的服务端这边汇总这些Span 并拼接出一条请求链路,那么这里就存在一个问题,客户端得到的Span 如何给到服务端,通常是会在每个Span 调用finish() 方法时将Span 发送给服务端,这里的发送有多种形式,例如把Span 主动的pushKafkaTopic ,还例如把Span 当作一条日志打印出来再由Filebeat 采集,我们的本系列文章中,就选择将Span链路日志的形式打印出来,至于如何采集以及服务端如何拼接,这不在本系列文章的讨论范围内。

正文

这里直接给出定义好的链路日志格式,如下所示。

json 复制代码
{
    "traceId": "testTraceId", // 当前节点所属链路的Id
    "spanId": "testSpanId", // 当前节点的SpanId
    "parentSpanId": "testparentSpanId", // 当前节点的父节点的SpanId
    "timestamp": "1704038400000", // 接收到请求那一刻的毫秒时间戳
    "duration": "10", // 表示接收请求到响应请求的耗时
    "httpCode": "200", // 请求的HTTP状态码
    "host": "127.0.0.1", // 当前节点的主机地址
    "requestStacks": [ // 请求堆栈
        {
            "subSpanId": "testSubSpanId", // 当前节点的子节点的SpanId
            "subHttpCode": "200", // 请求子节点的HTTP状态码
            "subTimestamp": "1704038401000", // 当前节点请求子节点的毫秒时间戳
            "subDuration": "5", // 表示发起请求到收到响应的耗时
            "subHost": "192.168.10.5", // 当前节点的子节点的主机地址
        }
    ]
}

特别说明一下requestStacks 这个字段,该字段主要就是用于记录当前节点调用下游子节点的Span 的信息,包括子节点的SpanId ,调用子节点得到的HTTP状态码和调用耗时等。

既然确定了链路日志的格式,现在我们用一个示例demo ,来结合链路日志做一个演示说明。示例demo的调用链路如下所示。

假定请求在网络中跑不耗时,clientserver1 的应用自身逻辑处理不耗时,那么对于client,打印的链路日志如下。

json 复制代码
{
    "traceId": "0001",
    "spanId": "01",
    "parentSpanId": "0",
    "timestamp": "1704038400000",
    "duration": "100",
    "httpCode": "200",
    "host": "192.168.10.1",
    "requestStacks": [
        {
            "subSpanId": "02",
            "subHttpCode": "200",
            "subTimestamp": "1704038400000",
            "subDuration": "40",
            "subHost": "192.168.10.2"
        },
        {
            "subSpanId": "04",
            "subHttpCode": "200",
            "subTimestamp": "1704038400040",
            "subDuration": "60",
            "subHost": "192.168.10.3"
        }
    ]
}

对于server1,打印链路日志如下。

json 复制代码
{
    "traceId": "0001",
    "spanId": "02",
    "parentSpanId": "01",
    "timestamp": "1704038400000",
    "duration": "40",
    "httpCode": "200",
    "host": "192.168.10.2",
    "requestStacks": [
        {
            "subSpanId": "03",
            "subHttpCode": "200",
            "subTimestamp": "1704038400000",
            "subDuration": "40",
            "subHost": "192.168.10.4"
        }
    ]
}

对于server2,打印链路日志如下。

json 复制代码
{
    "traceId": "0001",
    "spanId": "04",
    "parentSpanId": "01",
    "timestamp": "1704038400040",
    "duration": "60",
    "httpCode": "200",
    "host": "192.168.10.3",
    "requestStacks": []
}

对于server3,打印链路日志如下。

json 复制代码
{
    "traceId": "0001",
    "spanId": "03",
    "parentSpanId": "02",
    "timestamp": "1704038400000",
    "duration": "40",
    "httpCode": "200",
    "host": "192.168.10.4",
    "requestStacks": []
}

总结

其实打印链路日志,其核心目的就是记录每个SpantraceIdspanIdparentSpanId ,通过这三个字段信息,就可以拼接出一条链路。此外,还可以根据实际的需求添加一些额外字段,例如和时间相关的durationtimestamp,这两个字段能够帮助排查链路中的耗时情况。

相关推荐
tg-zm88999621 小时前
2025返利商城源码/挂机自动收益可二开多语言/自定义返利比例/三级分销理财商城
java·mysql·php·laravel·1024程序员节
X***C86221 小时前
SpringBoot:几种常用的接口日期格式化方法
java·spring boot·后端
i***t91921 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
前端达人21 小时前
你的App消息推送为什么石沉大海?看Service Worker源码我终于懂了
java·开发语言
小光学长1 天前
基于ssm的宠物交易系统的设计与实现850mb48h(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·前端·数据库
编程大师哥1 天前
vxe-table 透视表分组汇总及排序基础配置
java
8***84821 天前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
o***74171 天前
基于SpringBoot的DeepSeek-demo 深度求索-demo 支持流式输出、历史记录
spring boot·后端·lua
9***J6281 天前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
S***q1921 天前
Rust在系统工具中的内存安全给代码上了三道保险锁。但正是这种“编译期的严苛”,换来了运行时的安心。比如这段代码:
开发语言·后端·rust