Tracing 是一种用于跟踪和记录系统运行状态、事件或资源使用情况的技术,广泛应用于软件开发、性能优化、故障排查等领域。以下是 tracing 中的一些关键概念、应用及跟 log 之间的关系。
Tracing 概念
🔍 Tracing 基础概念梳理
🌲 整体结构(树形)
- 一个请求的完整追踪过程被称为一个 Trace,可以用一个 trace_id 唯一标识。
- 一个 Trace 是由多个 Span 组成的,Span 形成一棵树结构。
- 根节点是最早创建的那个 Span(通常是入口请求,如前端或网关)。
- 子节点 Span 表示在处理过程中产生的后续调用(如微服务之间的调用、数据库访问等)。
🧩 核心概念一览
概念 | 描述 |
---|---|
Trace | 表示一次完整的请求链,一般由一个唯一的 trace_id 标识。可以跨多个服务。 |
Span | Trace 中的一个操作单位,表示一次具体的调用或操作,有 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 是可以相互嵌套的,它天生就是为了表现“一个操作内部还包含其他子操作”这种结构。它之所以是树结构,就是因为服务调用本身、或某个操作本身,就常常是“嵌套”的。
|
|
⏱ 耗时信息的采集
每个 Span 都记录 start & end 时间,比如
|
|
✅ 计算方式:duration = end_time - start_time(建议保留 ms 精度)
✨ 补充说明
- Span 使用
- Span != 服务,一个服务内可以产生多个 span,比如控制器、服务、DAO 等不同层级调用。
- Span 的嵌套并不完全等于服务调用,可以是方法内的子步骤。
- Baggage 不适合放太多数据,因为会被全链路传播,性能和隐私要注意。
一些应用
展示耗时信息(前端可视化)
1 、树形结构 + 耗时
- 展示结构 + 每一层的耗时:
|
|
2 、时间线图 / 甘特图(推荐)
- 横轴是时间,纵轴是调用树层级,像这样:
|
|
- 可以一眼看出:
- 谁最耗时
- 哪些操作是串行的、哪些是并行的
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 技术将错误信息记录下来。
- 没有严格的包含关系:
- 它们各自服务于不同的场景和技术需求,但两者并不互相覆盖所有功能。
核心能力对比
维度 | Logging | Tracing |
---|---|---|
📍 关注点 | 记录本地事件(debug/info/error) | 跨服务的请求链路与时序 |
🧩 粒度 | 每一行日志就是一条信息 | 每个 trace 是一个整体请求,包含多个 span |
🔍 定位问题 | 看错误堆栈、日志内容 | 看请求链在哪个环节耗时多/出错 |
⏱ 时间维度 | 一条日志有个 timestamp | Trace 有 start/end + 每个 Span 有耗时 |
🌐 上下文 | 通常只看当前服务 | 可以跨多个服务传播 context(trace_id, baggage) |
📈 可视化 | 日志系统展示(Elasticsearch, Loki) | 链路图、时间线(如 Jaeger, Zipkin) |
🧰 典型工具 | Log4j、SLF4J、Winston、Loki | OpenTelemetry、Jaeger、SkyWalking |
软件开发视角
1. Tracing 的作用
- 追踪应用程序的行为:Tracing 主要用于跟踪应用程序的运行状态、资源使用情况(如 CPU、内存)、错误发生等。
- 实时监控:常用于实时应用中,例如浏览器插件、云服务监控等。
2. Logging 的作用
- 记录详细信息:Logging 是记录日志文件或直接发送到云服务的详细信息(如错误堆栈、调用链)。
- 调试和分析:用于调试错误源,并提供详细的运行轨迹。
3. Tracing 和 Logging 的关系
- 两者其实是可以相辅相成的。一个系统通常这样用:
- Tracing → 提供“全局视角”:看到某个请求走了哪些服务、在哪一步慢了或失败了
- Logging → 提供“局部细节”:具体方法内部执行了什么、变量值、堆栈、报错
- 举例说明下,比如 Tracing 信息如下:
|
|
- Logging 里,有如下信息:
|
|
当看到 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 看整体健康趋势