Featured image of post Tracing 初步理解

Tracing 初步理解

简单介绍 Tracing技术,它是一种用于跟踪和记录系统运行状态、事件或资源使用情况的技术,广泛应用于软件开发、性能优化和故障排查等领域。

Tracing 是一种用于跟踪和记录系统运行状态、事件或资源使用情况的技术,广泛应用于软件开发、性能优化、故障排查等领域。以下是 tracing 中的一些关键概念、应用及跟 log 之间的关系。

Tracing 概念

🔍 Tracing 基础概念梳理

🌲 整体结构(树形)

  • 一个请求的完整追踪过程被称为一个 Trace,可以用一个 trace_id 唯一标识。
  • 一个 Trace 是由多个 Span 组成的,Span 形成一棵树结构
    • 根节点是最早创建的那个 Span(通常是入口请求,如前端或网关)。
    • 子节点 Span 表示在处理过程中产生的后续调用(如微服务之间的调用、数据库访问等)。

🧩 核心概念一览

概念描述
Trace表示一次完整的请求链,一般由一个唯一的 trace_id 标识。可以跨多个服务。
SpanTrace 中的一个操作单位,表示一次具体的调用或操作,有 span_id。可以嵌套形成父子关系。
Parent Span一个 Span 可以有一个父 Span(用 parent_span_id 标识),用于构建调用树。
Child Span父 Span 内部调用的子操作,属于同一个 trace_id,形成树形结构。
Baggage是附着在 Trace 上的键值对,会随着 trace 传播给每个后续的 span,可用于传递业务上下文数据(如用户 ID、地域等)
Attributes / Tags是附着在 Span 上的键值对,用于记录执行时的状态、参数信息等。
Events / Logs是附着在 Span 上的时间序列事件,例如某个时刻报错、收到响应等。

Span

🧠 每个 Span 都包含什么?

  • span_id:该 Span 的唯一标识
  • trace_id:所属的 Trace ID
  • parent_span_id:它的父节点 Span 的 ID(根 span 没有父 ID)
  • name:这个 span 的名称(如“调用用户服务”)
  • start_time / end_time:起止时间
  • attributes(也叫 tags):键值对,描述上下文或运行参数
  • events(或 logs):事件序列(如异常、返回值)
  • status:表示是否成功(比如 HTTP 500)
  • baggage(选):附加在 trace 上传播的键值对(所有 span 可共享)
    但如果你每个小操作都创建一个 span,会导致:
  • 数据太多,观察成本高
  • 采样/存储开销大
    所以建议:
  • 只给 关键路径加 span,比如:IO 操作、外部调用、耗时步骤。
  • 对短暂或无关紧要的内部操作(如计算、循环等)不需要单独创建 span。

🧬 数据流示意(树结构)

Span 是可以相互嵌套的,它天生就是为了表现“一个操作内部还包含其他子操作”这种结构。它之所以是树结构,就是因为服务调用本身、或某个操作本身,就常常是“嵌套”的。

1
2
3
4
5
6
7
Trace (trace_id: abc123)
└── Span A (root span, span_id: 1)
    ├── Span B (span_id: 2, parent: 1)
    │   ├── Span D (span_id: 4, parent: 2)
    │   └── Span E (span_id: 5, parent: 2)
    └── Span C (span_id: 3, parent: 1)

⏱ 耗时信息的采集

每个 Span 都记录 start & end 时间,比如

1
2
3
4
5
6
{
  "span_id": "abc123",
  "start_time": "2025-04-13T10:00:00.123Z",
  "end_time": "2025-04-13T10:00:01.456Z",
  "duration_ms": 1333
}

✅ 计算方式:duration = end_time - start_time(建议保留 ms 精度)

✨ 补充说明

  • Span 使用
    • Span != 服务,一个服务内可以产生多个 span,比如控制器、服务、DAO 等不同层级调用。
    • Span 的嵌套并不完全等于服务调用,可以是方法内的子步骤。
    • Baggage 不适合放太多数据,因为会被全链路传播,性能和隐私要注意。

一些应用

展示耗时信息(前端可视化)

1 、树形结构 + 耗时

  • 展示结构 + 每一层的耗时:
1
2
3
4
5
6
└── 用户请求(1000ms)
    ├── 认证服务(200ms)
    ├── 用户服务(600ms)
    │   ├── 数据库查询(400ms)
    │   └── 缓存写入(50ms)
    └── 日志服务(100ms)

2 、时间线图 / 甘特图(推荐)

  • 横轴是时间,纵轴是调用树层级,像这样:
1
2
3
4
5
时间轴:  |----100ms----|----100ms----|----100ms----|…
用户请求:  [------------------------------]
用户服务:     [---------------]
数据库查询:   [------]
缓存写入:                   [--]
  • 可以一眼看出:
    • 谁最耗时
    • 哪些操作是串行的、哪些是并行的

3、判断性能瓶颈(基于耗时)

根据 trace 可以自动检测一些问题:

  • 某个 span 耗时超过阈值(如 200ms)
  • 同一个服务下某些 span 总是很慢
  • 多个 span 串行而不是并行执行(如 A → B → C)

✨ 补充说明

  • 统一时间来源
    • 使用同一个时钟源(如 performance.now() 或 process.hrtime(),不要用 Date.now() 混用)
    • 建议用 高精度时间(纳秒或微秒),落库前可以转成毫秒或浮点秒数。

Tracing 与 Logging 的关系

在软件开发中,Tracing(追踪)Logging(日志记录) 是两种常用的技术,尽管它们的功能相似,但在应用场景和实现方式上有显著差异。
一句话:Tracing 是用于“跨服务的调用链分析”,而 Logging 是用于“记录事件和调试信息”。

TL; DR

  • Tracing 和 Logging 是互补的技术
    • Tracing 更适合实时监控和动态数据上报,而 Logging 则适合长期记录和调试。
    • 它们可以结合使用:例如,在应用运行期间使用 Tracing 技术获取实时数据(如资源使用情况),然后通过 Logging 技术将错误信息记录下来。
  • 没有严格的包含关系
    • 它们各自服务于不同的场景和技术需求,但两者并不互相覆盖所有功能。

核心能力对比

维度LoggingTracing
📍 关注点记录本地事件(debug/info/error)跨服务的请求链路与时序
🧩 粒度每一行日志就是一条信息每个 trace 是一个整体请求,包含多个 span
🔍 定位问题看错误堆栈、日志内容看请求链在哪个环节耗时多/出错
⏱ 时间维度一条日志有个 timestampTrace 有 start/end + 每个 Span 有耗时
🌐 上下文通常只看当前服务可以跨多个服务传播 context(trace_id, baggage)
📈 可视化日志系统展示(Elasticsearch, Loki)链路图、时间线(如 Jaeger, Zipkin)
🧰 典型工具Log4j、SLF4J、Winston、LokiOpenTelemetry、Jaeger、SkyWalking

软件开发视角

1. Tracing 的作用

  • 追踪应用程序的行为:Tracing 主要用于跟踪应用程序的运行状态、资源使用情况(如 CPU、内存)、错误发生等。
  • 实时监控:常用于实时应用中,例如浏览器插件、云服务监控等。

2. Logging 的作用

  • 记录详细信息:Logging 是记录日志文件或直接发送到云服务的详细信息(如错误堆栈、调用链)。
  • 调试和分析:用于调试错误源,并提供详细的运行轨迹。

3. Tracing 和 Logging 的关系

  • 两者其实是可以相辅相成的。一个系统通常这样用:
    • Tracing → 提供“全局视角”:看到某个请求走了哪些服务、在哪一步慢了或失败了
    • Logging → 提供“局部细节”:具体方法内部执行了什么、变量值、堆栈、报错
  • 举例说明下,比如 Tracing 信息如下:
1
2
3
TraceID: abc123
Span: [用户服务处理订单] 150ms
└── [调用库存服务] 90ms ❌ Timeout
  • Logging 里,有如下信息:
1
2
[WARN]TraceID=abc123 无法连接库存数据库
[ERROR] TraceID=abc123 查询失败,返回500

当看到 trace 知道问题出在“调用库存服务”,再用 trace_id 去日志里一搜,就能看到详细原因。

替代性和建议

✅ 理论上可以把 Tracing 当 Log 用

如 OpenTelemetry 就是一个记录很细的系统,可以当 log 用。

  • Tracing 的每个 span 可以附加 事件(event)/ 日志(log)
  • 可以做到类似 logger.info(…) 的功能
  • 可以把 span.addEvent(" 准备开始数据库查询 “) 当作 log

❌ 但不建议只用 Tracing 替代 Logging

  • Tracing 会采样(不是所有请求都记录)→ 有的错误日志你找不到
  • 写日志的自由度高(结构、内容、格式),Tracing 更偏结构化
  • 日志可以本地保存、不依赖全链路链路系统
  • Tracing 更适合宏观链路分析,不适合长期审计、安全排查等用途

🧠 推荐做法(实际工程中)

级别工具用途
🔭 全局观Tracing (OpenTelemetry)分布式链路追踪,定位请求瓶颈
🛠 细节Logging (Winston, Logback…)开发调试、审计、错误定位
📊 聚合Metrics (Prometheus…)吞吐量、请求数、错误率等统计指标

在写日志时 附带 trace_id 是非常推荐的做法(比如 MDC、trace context),这样就可以轻松做到 “trace 定位后,日志深入排查”。比如在 log 里统一打出:[INFO][trace_id=abc123]查询成功
一个请求来了,你可以:

  • 用 Trace 找到问题在哪个服务
  • 用 Log 看服务内部报错细节
  • 用 Metrics 看整体健康趋势
Built with Hugo
Theme Stack designed by Jimmy