跟踪上下文第 2 级

W3C 候选推荐草案

关于本文档的更多详细信息
本版本:
https://www.w3.org/TR/2024/CRD-trace-context-2-20240328/
最新发布版本:
https://www.w3.org/TR/trace-context-2/
最新编辑草案:
https://w3c.github.io/trace-context/
历史:
https://www.w3.org/standards/history/trace-context-2/
提交历史
实现报告:
https://w3c.github.io/trace-context/implementations
编辑:
Sergey KanzhelevGoogle
Daniel DylaDynatrace
Yuri ShkuroMeta
J. Kalyana SundaramMicrosoft
Bastian KrolIBM
曾任编辑:
Nik Molnar
Alois Reitbauer
Morgan McLean
Bogdan Drutu
Daniel Khan
反馈:
GitHub w3c/trace-context拉取请求新议题未解决议题
public-trace-context@w3.org 主题行请写 trace-context存档
讨论
我们在 Slack 上。

摘要

本规范定义了标准 HTTP 标头以及一种值格式,用于传播能够支持分布式跟踪场景的上下文信息。 本规范标准化了上下文信息在服务之间发送和修改的方式。上下文信息唯一标识分布式 系统中的各个请求,并且还定义了一种添加和传播提供者特定上下文信息的方法。

本文档的状态

本节描述的是本文档在发布时的状态。当前 W3C 发布物列表以及本技术报告的最新修订版可在 https://www.w3.org/TR/ 上的 W3C 技术 报告索引中找到。

这个新版本增加了关于 trace-idspan-id 字段生成的考虑事项,并增加了随机 trace ID 标志。

作为退出标准,工作组打算使用测试套件,展示至少 2 个采用并使用本 规范的实现。

本文档由分布式 跟踪工作组作为候选推荐草案,使用 推荐标准轨道发布。

作为候选推荐发布并不 意味着得到 W3C 及其成员的认可。候选 推荐草案整合了工作组 打算纳入后续候选推荐快照的、相对于上一份候选推荐的更改。

本文档是一份草案文档,可能随时被其他 文档更新、替换或废弃。不应将本文档作为 进行中的工作以外的内容引用。

本文档由一个按照 W3C 专利 政策运作的小组制定。 W3C 维护一份 与该小组交付物相关的任何专利 披露的公开列表; 该页面还包括披露专利的说明。实际 知悉某项专利、且该个人认为该专利包含 必要权利要求的个人, 必须按照 W3C 专利政策第 6 节 披露相关信息。

本文档受 2023 年 11 月 03 日 W3C 流程文档约束。

1. 一致性

除标记为非规范性的各节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。 本规范中的其他所有内容均为规范性内容。

本文档中的关键词 MAYMUSTMUST NOTSHOULDSHOULD NOT 应按 BCP 14 [RFC2119] [RFC8174] 中所述解释,且仅当它们像这里所示那样以全大写形式出现时才如此解释。

2. 概述

2.1 问题陈述

分布式跟踪是一种由跟踪工具实现的方法,用于跨多个软件组件跟踪、分析和调试一个 事务。通常,一个分布式跟踪 会穿越多个组件,因此要求它能够在所有参与 系统中被唯一标识。跟踪上下文传播会传递这种唯一标识。如今,跟踪上下文 传播由每个跟踪供应商分别实现。在多供应商环境中,这 会导致互操作性问题,例如:

过去,这些问题并没有产生重大影响,因为大多数应用都由单一 跟踪供应商监控,并且停留在单一平台提供者的边界之内。如今,越来越多的应用 是高度分布式的,并利用多个中间件服务和 云平台。

现代应用的这种转变需要一个分布式跟踪上下文传播标准。

2.2 解决方案

跟踪上下文规范定义了一种通用约定的格式,用于交换跟踪 上下文传播数据——称为跟踪上下文。跟踪上下文通过以下方式解决 上述问题:

用于传播跟踪数据的统一方法提高了对分布式 应用行为的可见性,有助于问题和性能分析。跟踪 上下文提供的互操作性是管理现代基于微服务的应用的前提条件。

Trace Context 规范的当前版本面向应用和服务的实现, 包括在浏览器中运行的 Web 应用。Web 浏览器或用户代理目前 不在目标实现范围内。

2.3 设计概述

跟踪上下文被拆分为两个独立的传播字段,用以支持互操作性和 供应商特定的可扩展性:

跟踪工具在与跟踪上下文交互时可以提供两个级别的一致行为:

跟踪工具可以针对它所监控组件的每个单独请求选择改变这种行为。

3. 跟踪上下文 HTTP 请求 标头格式

本节描述分布式跟踪上下文到 traceparenttracestate HTTP 标头的绑定。

3.1 标头之间的 关系

traceparent 请求标头以一种所有供应商都能理解的 通用格式表示跟踪系统中的传入请求。下面是一个 traceparent 标头示例。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

tracestate 请求标头包含一个可能采用供应商特定格式表示的父项:

tracestate: congo=t61rcWkgMzE

例如,假设系统中的客户端和服务器使用不同的跟踪供应商:Congo 和 Rojo。一个 在 Congo 系统中被跟踪的客户端会向一个出站 HTTP 请求添加以下标头。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: congo=t61rcWkgMzE

:在这种情况下,tracestatet61rcWkgMzE 是 对父 ID(b7ad6b7169203331)进行 Base64 编码后的结果,尽管并不要求进行这种处理。

在 Rojo 跟踪系统中被跟踪的接收服务器会保留它收到的 tracestate,并在左侧添加一个新条目。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01
tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE

你会注意到,Rojo 系统在其 tracestate 条目中复用了其 traceparent 的值。这意味着它是一个通用跟踪系统(没有传递 专有信息)。否则,tracestate 条目是不透明的,并且可以是 供应商特定的。

如果下一个接收服务器使用 Congo,它会保留来自 Rojo 的 tracestate,并在前一个条目的左侧 为父项添加一个新条目。

traceparent: 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01
tracestate: congo=ucfJifl5GOE,rojo=00f067aa0ba902b7

注:ucfJifl5GOE 是经过 Base64 编码的父 ID b9c7c989f97918e1

注意,当 Congo 写入其 traceparent 条目时,它没有进行编码,这有助于 做关联的人保持一致性。然而,它的 tracestate 条目的值是经过编码的,并且 不同于 traceparent。这是可以的。

最后,你会看到 tracestate 完全按原样保留了 Rojo 的条目,只是被推到了 右侧。最左侧的位置让下一个服务器知道哪个跟踪系统对应于 traceparent。在这种情况下,因为 Congo 写入了 traceparent,所以它的 tracestate 条目应该位于最左侧。

3.2 Traceparent 标头

traceparent HTTP 标头字段标识跟踪系统中的传入请求。它 有四个字段:

3.2.1 标头名称

标头名称:traceparent

该标头名称是 ASCII 大小写不敏感的。也就是说,TRACEPARENTTraceParenttraceparent 被认为是同一个标头。该标头名称是单个词;它 不包含任何分隔符,例如连字符。

为了提高跨多种协议的互操作性并鼓励成功集成, 跟踪系统 SHOULD 将标头名称编码为 ASCII 小写

3.2.2 traceparent 标头 字段值

本节使用 [RFC5234] 的扩展巴科斯-诺尔范式(ABNF)记法,包括该文档中的 DIGIT 规则。DIGIT 规则定义单个数字字符 0-9

HEXDIGLC = DIGIT / "a" / "b" / "c" / "d" / "e" / "f" ; lowercase hex character
value           = version "-" version-format

短横线(-)字符用作字段之间的分隔符。

3.2.2.1 version
version         = 2HEXDIGLC   ; this document assumes version 00. Version ff is forbidden

版本(version)是一个 8 位无符号整数值,序列化为包含两个字符的 ASCII 字符串。 版本 255("ff")无效。本文档指定 traceparent 标头的版本 0("00")。

3.2.2.2 version-format

以下 version-format 定义用于版本 00

version-format   = trace-id "-" parent-id "-" trace-flags
trace-id         = 32HEXDIGLC  ; 16 bytes array identifier. All zeroes forbidden
parent-id        = 16HEXDIGLC  ; 8 bytes array identifier. All zeroes forbidden
trace-flags      = 2HEXDIGLC   ; 8 bit flags.
3.2.2.3 trace-id

这是整个跟踪森林的 ID,用于通过一个系统唯一标识一个分布式跟踪。 它表示为一个 16 字节数组,例如 4bf92f3577b34da6a3ce929d0e0e4736。 所有字节均为零(00000000000000000000000000000000)被视为无效 值。

trace-id 的值 SHOULD 是全局唯一的。 一种推荐方法是在足够确定的程度上保证全局唯一性,并处理一些隐私和 安全方面的考虑,即随机(或伪随机)生成 trace-id。 实现者 SHOULD 使用一种 trace-id 生成方法,该方法 至少随机(或伪随机)生成该 ID 最右侧的 7 个字节。 如果最右侧的 7 个字节是随机(或伪随机)生成的,则相应的随机 trace id 标志 SHOULD 被 设置。 有关更多详细信息,参见trace-id 字段生成的 考虑事项

如果 trace-id 值无效(例如,如果它包含不允许的字符 或全为零),供应商 MUST 忽略 traceparent

3.2.2.4 parent-id

这是调用方所知的此请求的 ID(在某些跟踪系统中,这称为 span-id,其中 span 是一次客户端请求的执行)。它 表示为一个 8 字节数组,例如 00f067aa0ba902b7。所有字节均为零 (0000000000000000)被视为无效值。

parent-id 无效(例如,如果它包含非小写十六进制字符)时, 供应商 MUST 忽略 traceparent

3.2.2.5 trace-flags

这是一个8 位字段, 用于控制采样、跟踪级别等跟踪标志。这些标志是调用方给出的建议, 而不是必须遵循的严格规则,原因有三点:

  1. 不受信任的调用方可能能够通过恶意设置这些标志来滥用跟踪系统。
  2. 调用方可能存在导致跟踪系统出现问题的 bug。
  3. 调用方服务和被调用方服务之间负载不同,可能迫使被调用方进行降采样。

你可以在本规范的安全考虑事项一节中找到更多内容。

与其他字段一样,trace-flags 是十六进制编码的。例如,所有 8 个标志 都设置时为 ff,没有设置标志时为 00

由于这是一个位字段,因此不能通过简单的相等比较来解释这些标志。 例如,0100000001)和 0300000011)都表示该跟踪已被采样,因为 sampled 标志 (00000001)被设置;而 030200000010) 都表示 trace-id 至少最右侧的 7 个字节是随机 (或伪随机)生成的,因为 random 位(00000010)被设置。 解释位字段时常见的错误是比较整个数字,而不是 解释单个位。

下面是正确处理 trace flags 的示例:

static final byte FLAG_SAMPLED = 1; // 00000001
static final byte FLAG_RANDOM = 2; // 00000010
...
boolean sampled = (traceFlags & FLAG_SAMPLED) == FLAG_SAMPLED;
boolean random = (traceFlags & FLAG_RANDOM) == FLAG_RANDOM;
3.2.2.5.1 Sampled 标志

设置时,最低有效位(最右侧)表示调用方可能已记录 跟踪数据。未设置时,调用方没有带外记录跟踪数据。

有许多记录场景可能会破坏分布式跟踪:

  • 仅记录请求的一个子集会导致跟踪中断。
  • 在负载下,记录所有传入和传出请求的信息会变得极其 昂贵。
  • 作出随机或组件特定的数据收集决策会导致所有跟踪中的数据 碎片化。

由于这些问题,跟踪供应商会作出自己的记录决策,并且对于完成此任务的最佳算法 并没有共识。

各种技术包括:

  • 概率采样(通过抛硬币从 100 个分布式跟踪中采样 1 个)
  • 延迟决策(根据持续时间或请求结果作出收集决策)
  • 延期采样(让被调用方决定是否需要收集关于此请求的信息)

这些技术的实现方式可以是跟踪供应商特定的,也可以由应用定义。

tracestate 字段被设计用于处理针对给定供应商作出 记录决策(或其他特定信息)的各种技术。 sampled 标志提供了更好的供应商间互操作性。它允许 供应商传达记录决策,并为客户提供更好的体验。

例如,当 SaaS 服务参与一个分布式跟踪时,该服务并不知道 其调用方使用的是哪个跟踪供应商。该服务可能会出于监控或故障排查目的生成 传入请求的记录。sampled 标志可用于确保调用方标记为需要记录的 请求的信息也会被下游的 SaaS 服务记录,以便调用方可以排查 每个已记录请求的行为。

sampled 标志除只能在parent-id 被更新时 被改变外,对其改变没有其他限制。

以下是一组供应商 SHOULD 使用的建议, 用于提高供应商互操作性。

  • 如果组件作出了确定性的记录决策——该决策 SHOULD 反映在 sampled 标志中。
  • 如果组件需要作出记录决策——它 SHOULD 尊重 sampled 标志值。 安全考虑事项 SHOULD 被应用,以防止该标志被滥用或恶意使用。
  • 如果组件延期或延迟了决策,并且只会记录遥测的一个子集, 则 sampled 标志应该保持不变地传播。当跟踪由该组件发起时, 它应该默认设置为 0

还有两个额外选项,供应商 MAY 遵循:

  • 作出延期或延迟记录决策的组件可以通过将请求子集的 sampled 标志设置为 1 来传达记录优先级。
  • 组件也可以回退到概率采样,并将请求子集的 sampled 标志设置为 1
3.2.2.5.2 随机 Trace ID 标志

trace-flags 字段的次低有效位表示 random-trace-id 标志。

在开始或重新开始一个跟踪时(即,当参与者生成新的 trace-id 时),适用以下规则:

  • 如果该标志被设置,则 trace-id 至少最右侧的 7 个字节 MUST 在区间 [0..2^56-1] 上以均匀 分布随机(或伪随机)选择。
  • 如果该标志未设置,trace-id MAY 仍然是 随机(或伪随机)生成的。
  • 未设置时,trace-id MAY 以任何 满足trace ID 格式要求的方式生成。
  • trace-id 至少最右侧的 7 个字节是随机(或 伪随机)生成的时,random-trace-id 标志 SHOULD 被设置为 1

在继续一个跟踪时(即,传入 HTTP 请求带有 traceparent 标头,并且参与者在出站请求的 traceparent 标头中使用相同的 trace-id),适用以下规则:

  • 如果传入 traceparent 标头中的该标志已设置,则在所有使用相同 trace-id 的出站 traceparent 标头中它 MUST 也被设置。
  • 如果传入 traceparent 标头中的该标志未设置,则在任何使用相同 trace-id 的出站 traceparent 标头中它 MUST 也未设置。

这允许下游消费者基于这些字节实现诸如跟踪采样或数据库 分片之类的功能。 有关更多信息,参见trace-id 字段生成的考虑事项

3.2.2.5.3 其他标志

其他标志(例如 00000100)的行为未定义,并保留 供将来使用。供应商 MUST 将这些标志设置为零。

3.2.3 HTTP traceparent 标头示例

调用方对该请求进行了采样时的有效 traceparent:

Value = 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
base16(version) = 00
base16(trace-id) = 4bf92f3577b34da6a3ce929d0e0e4736
base16(parent-id) = 00f067aa0ba902b7
base16(trace-flags) = 01  // sampled

调用方未对该请求进行采样时的有效 traceparent:

Value = 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00
base16(version) = 00
base16(trace-id) = 4bf92f3577b34da6a3ce929d0e0e4736
base16(parent-id) = 00f067aa0ba902b7
base16(trace-flags) = 00  // not sampled

3.2.4 traceparent 的版本控制

本规范对跟踪上下文的未来版本持有明确意见。本 规范的当前版本假设 traceparent 标头的未来版本将以增量方式 扩展当前版本。

在解析具有意外格式的标头时,供应商 MUST 遵循这些规则:

  • 透传服务不应分析版本。它们应该预期将来的标头可能 具有更大的大小限制,并且只拒绝过大的标头。

  • 当版本前缀无法解析时(它不是 2 个十六进制字符后跟短横线 (-)),实现应该重新开始跟踪。

  • 如果检测到更高版本,实现 SHOULD 尝试 通过以下方式解析它:

    • 如果标头长度短于 55 个字符,供应商不应解析该 标头,并应重新开始跟踪。
    • 解析 trace-id(从第一个短横线后开始,读取接下来的 32 个字符)。 供应商 MUST 检查这 32 个字符是否为十六进制,并且 后面跟着一个短横线(-)。
    • 解析 parent-id(从第 35 个位置的第二个短横线后开始, 读取接下来的 16 个字符)。供应商 MUST 检查这 16 个字符是否为 十六进制,并且后面跟着一个短横线。
    • 解析 flagssampled 位(从第三个 短横线后读取 2 个字符)。供应商 MUST 检查这 2 个字符要么位于 字符串末尾,要么后面跟着一个短横线。

    如果三个值都成功解析,供应商应该使用它们。

供应商 MUST NOT 解析或假定此 版本中的未知字段有任何含义。供应商 MUST 使用这些字段按照 实现所知的最高版本规范(在本规范中为 00)构造新的 traceparent 字段。

3.3 Tracestate 标头

tracestate HTTP 标头的主要目的是在不同分布式跟踪系统之间提供附加的 供应商特定跟踪标识信息,并且它是 traceparent 字段的配套标头。 它还传达请求在多个分布式跟踪图中的位置信息。

如果供应商未能解析 traceparent,则它 MUST NOT 尝试 解析 tracestate。注意,反过来并不成立:解析 tracestate 失败 MUST NOT 影响 traceparent 的解析。

tracestate HTTP 标头 MUST NOT 用于任何 不是由跟踪系统定义的属性。[BAGGAGE] MAY 可用于定义和传播此类应用级属性。

3.3.1 标头名称

标头名称:tracestate

该标头名称是 ASCII 大小写不敏感的。也就是说,TRACESTATETraceStatetracestate 被认为是同一个标头。该标头名称是单个词,它 不包含任何分隔符,例如连字符。

为了提高跨多种协议的互操作性并鼓励成功集成, 跟踪系统 SHOULD 将标头名称编码为 ASCII 小写

3.3.2 tracestate 标头 字段值

tracestate 字段可以在任何键中包含任意不透明值。 Tracestate MAY 作为多个标头字段发送或接收。 多个 tracestate 标头字段 MUST 按照 RFC9110 第 5.3 节字段 顺序中规定的方式处理。tracestate 标头 SHOULD 在 可能时作为单个字段发送,但 MAY 被拆分为多个标头字段。 当将 tracestate 作为多个标头字段发送时,它 MUST 按照 RFC9110 进行拆分。 当接收多个 tracestate 标头字段时,它们 MUST 按照 RFC9110 合并为单个标头。

本节使用 [RFC5234] 的扩展巴科斯-诺尔范式(ABNF)记法,包括 RFC5234 附录 B.1 中的 DIGIT 规则。它还包括 RFC9110 第 5.6.3 节中的 OWS 规则。

DIGIT 规则定义数字 0-9

OWS 规则定义一个可选的空白字符。为了提高可读性,它 用在可能出现零个或多个空白字符的位置。

调用方 SHOULD 将可选空白生成为一个空格; 否则,调用方 SHOULD NOT 生成可选空白。详情参见 相应的 RFC

tracestate 字段值是由逗号(,)分隔的 list-memberslistlist-member 是由等号 (=)分隔的键/值对。围绕 list-member 的空格和水平制表符会被忽略。 一个 list 中最多可以有 32 个 list-member。如果添加条目 会导致 tracestate 列表包含超过 32 个 list-members,则 应从列表中移除最右侧的 list-member

允许空的以及仅包含空白的列表成员。供应商 MUST 接受 空的 tracestate 标头,但 SHOULD 避免发送它们。 tracestate 中允许空列表成员,是因为当发送多个 tracestate 标头时,供应商很难识别空值。出于类似原因, 也允许空白字符,因为某些供应商会在逗号分隔符后自动注入空白, 即使在空标头的情况下也是如此。

3.3.2.1 list

一个包含两个 list-memberlist 的简单示例可能如下所示: vendorname1=opaqueValue1,vendorname2=opaqueValue2

list  = list-member 0*31( OWS "," OWS list-member )
list-member = (key "=" value) / OWS

list 的标识符是较短的(最多 256 个字符)文本标识符。

3.3.2.2 list-members

list-member 包含一个键/值对。

3.3.2.2.1 Key

键是描述供应商的标识符。

key = ( lcalpha / DIGIT ) 0*255 ( keychar )
keychar    = lcalpha / DIGIT / "_" / "-"/ "*" / "/" / "@"
lcalpha    = %x61-7A ; a-z

key MUST 以小写字母或数字开头,并 最多包含 256 个字符,包括小写字母(a-z)、 数字(0-9)、下划线(_)、短横线 (-)、星号(*)、正斜杠(/)和 at 符号 (@)。

3.3.2.2.2 Value

值是一个不透明字符串,包含最多 256 个可打印 ASCII [RFC0020] 字符(即 范围 0x20 到 0x7E),但不包括逗号(,)和(=)。该字符串必须以一个 不是空格(0x20)的字符结尾。注意,这也排除了制表符、换行符、回车符等。 所有前导空格 MUST 作为值的一部分保留。所有 尾随空格都被视为不属于值的可选空白字符。 在传播 标头时,MAY 排除可选尾随空白。

value    = 0*255(chr) nblk-chr
nblk-chr = %x21-2B / %x2D-3C / %x3E-7E
chr      = %x20 / nblk-chr

3.3.3 组合标头值

tracestate 值是跟踪图键/值对的串联。

示例:vendorname1=opaqueValue1,vendorname2=opaqueValue2

每个键只允许一个条目。例如,如果供应商名称为 Congo,且跟踪在 其系统中开始,随后经过名为 Rojo 的系统,之后又返回到 Congo,则 tracestate 值不会是:

congo=congosFirstPosition,rojo=rojosFirstPosition,congo=congosSecondPosition

相反,该条目会被重写为仅包含最近的位置: congo=congosSecondPosition,rojo=rojosFirstPosition

详情参见改变 tracestate 字段

3.3.3.1 tracestate 限制:

供应商 SHOULD 至少传播组合标头的 512 个字符。 该长度包括分隔列表项所需的逗号和可选空白 (OWS)字符。

在某些系统中,传播 512 个字符的 tracestate 可能 代价高昂。在这种情况下,所传播 tracestate 标头的最大大小 SHOULD 被记录并解释。传播 tracestate 的成本 SHOULD 与为最终用户启用的 监控场景的价值进行权衡。

在由于标头值总大小而截断 tracestate 的情况下, 供应商 MUST 截断整个条目。长度超过 128 个字符的条目 SHOULD 首先被移除。然后条目 SHOULDtracestate 末尾开始移除。 其他截断策略,例如安全列表条目、阻止列表条目或基于大小的 截断 SHOULD NOT 被使用。

3.3.4 tracestate HTTP 标头示例

单个跟踪系统(通用格式):

tracestate: rojo=00f067aa0ba902b7

多个跟踪系统(具有不同格式):

tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE

3.3.5 tracestate 的版本控制

tracestate 的版本由 traceparent 标头的版本前缀定义。 如果检测到更高版本,供应商需要尽其所能尝试解析 tracestate。 是否使用部分解析得到的 tracestate 键/值对由供应商决定。

3.4 改变 traceparent 字段

接收到没有 traceparent 标头的请求的供应商 SHOULD 为出站请求生成 traceparent 标头,从而有效地开始一个新的跟踪。 不这样做的一个可能原因可能是在性能敏感场景中,供应商决定 不采样某个请求。注意,对于大多数场景,即使不采样,也期望供应商生成该标头, 以便将采样决策传播到下游。

接收到 traceparent 请求标头的供应商 MUST 将其发送到 出站请求。它 MAY 在传递到 出站请求之前改变该标头的值。

如果 traceparent 字段的值在传播前未被更改, tracestate MUST NOT 也被修改。未修改的标头 传播通常在代理等透传服务中实现。这种行为也可以在当前不收集 分布式跟踪信息的服务中实现。

以下是允许的改变列表:

供应商 MUST NOTtraceparent 标头作出任何其他改变。

3.5 改变 tracestate 字段

接收到 tracestate 请求标头的供应商 MUST 将其发送到 出站请求。它 MAY 在传递到 出站请求之前改变该标头的值。改变 tracestate 时,未修改键/值对的顺序 MUST 被保留。修改后的键 MUST 被移动到列表的 开头(左侧)。

以下是允许的改变:

4. 处理模型

本节是非规范性的。

本节提供了一个逐步示例,说明跟踪供应商如何接收带有跟踪上下文 标头的请求,处理该请求,然后可能将其转发。该描述可在实现符合 跟踪上下文的跟踪系统、中间件(例如代理或消息 总线)或云服务时用作参考。

4.1 用于处理跟踪上下文请求标头的处理模型

该处理模型描述了修改并转发跟踪上下文 标头的供应商的行为。该模型如何工作取决于是否接收到 traceparent 标头。

4.1.1 未收到 traceparent

如果未收到 traceparent 标头:

  1. 供应商检查传入请求中是否有 traceparenttracestate 标头。
  2. 由于未收到 traceparent 标头,供应商会创建一个新的 trace-idparent-id,用于表示当前请求。(注:如果 供应商不采样该请求,并希望通过 sampled 标志将该采样决策 传达给下游,则供应商 MAY 创建不与任何实际跟踪 数据相关联的 trace-idparent-id。供应商 MAY 也可以决定不向下游传达该采样 决策。)
  3. 如果接收到没有伴随 traceparent 标头的 tracestate 标头,则它无效,并且 MUST 被丢弃。
  4. 供应商 SHOULD 创建新的 tracestate 标头并添加一个 新的键/值对。
  5. 供应商为出站请求设置 traceparenttracestate 标头。

4.1.2 收到 traceparent

如果收到 traceparent 标头:

  1. 供应商检查传入请求中是否有 traceparenttracestate 标头。
  2. 由于 traceparent 标头存在,供应商会尝试解析 traceparent 标头的版本。
    1. 如果版本无法解析,供应商会创建新的 traceparent 标头并删除 tracestate
    2. 如果版本号高于跟踪器支持的版本,供应商会使用 本规范中定义的格式(00)来解析 trace-idparent-id。 供应商只会解析本规范此版本支持的 trace-flags 值, 并忽略所有其他值。如果解析失败,供应商会创建 新的 traceparent 标头并删除 tracestate。供应商 将在出站请求中把所有未解析/未知的 trace-flags 设置为 0。
    3. 如果供应商支持该版本号,它会验证 trace-idparent-id。如果 trace-idparent-idtrace-flags 中任一无效,供应商会创建新的 traceparent 标头并删除 tracestate
  3. 供应商 MAY 验证 tracestate 标头。如果 tracestate 标头无法解析,供应商 MAY 丢弃 整个标头。无效的 tracestate 条目 MAY 也可以 被丢弃。
  4. 对于每个出站请求,供应商执行以下步骤:
    1. 供应商 MUST 修改 traceparent 标头:

      • 更新 parent-id属性 parent-id 的值 MUST 被设置为 表示当前操作 ID 的值。
      • 更新 sampledsampled 的值 反映调用方的记录行为。如果跟踪数据可能被记录, trace-flagssampled 标志值 MAY 被设置为 1,否则设置为 0。 设置该标志并不保证跟踪会被记录,但 会提高端到端记录跟踪的可能性。
    2. 供应商 MAY 修改 tracestate 标头:

      • 更新键值:可以更新任意键的值。 修改后的键 MUST 被移动到列表的开头(左侧)。
      • 添加新的键/值对:新的键-值对 MUST 被添加到列表的开头(左侧)。
      • 删除键/值对:任何键/值对 MAY 被删除。供应商 SHOULD NOT 删除不是由它们自己生成的键。删除任何 键/值对 MAY 破坏其他系统中的关联。
    3. 供应商为出站请求设置 traceparenttracestate 标头。

4.1.3 替代处理

上述处理模型描述了处理跟踪上下文标头的一整套步骤。 但是,在某些情况下,供应商可能只支持上述步骤的一个子集。 代理或消息中间件 MAY 决定不修改 traceparent 标头,而是移除无效标头或向 tracestate 添加附加信息。

5. 其他通信协议

虽然跟踪上下文是为 HTTP 定义的,但作者承认它也与其他通信 协议相关。本规范的扩展以及外部组织制定的规范 定义了其他协议中跟踪上下文序列化和反序列化的格式。注意,这些 扩展的成熟度级别可能与本规范不同。

请参阅 [trace-context-protocols-registry],了解 其他协议的跟踪上下文实现详情。

6. 隐私考虑事项

将标头传播到下游服务以及存储这些标头的值的要求,会带来 潜在的隐私顾虑。跟踪供应商 MUST NOTtraceparenttracestate 字段用于任何个人身份信息或其他敏感信息。 这些字段的唯一目的是启用跟踪关联。

供应商 MUST 评估标头滥用的风险。本节提供一些 考虑事项以及对存储和传播这些标头相关风险的初步评估。 跟踪供应商可以选择在允许跟踪系统执行可能传播或存储这些字段的代码之前, 检查并移除字段中的敏感信息。不过,所有改变都应 符合本规范中定义的改变列表。

6.1 traceparent 字段的隐私

traceparent 字段 MUST NOT 包含任何个人身份 信息。实现这一点的一种方式是使用不会暴露任何个人身份信息的随机数 生成器来随机生成所有 trace ID。任何用于生成 trace ID 的随机数生成器 MUST NOT 依赖任何可能具有个人身份属性的信息作为输入或种子 状态。

traceparent 字段的另一个隐私风险,是能够关联作为 单个事务一部分发出的请求。下游服务可能会跟踪并关联 单个事务中发出的两个或更多请求,并可能基于另一个请求中的 信息对某个请求调用方的身份作出推断。

注意,traceparent 字段的这些隐私顾虑是理论性的,而非 实际性的。某些发起或接收请求的服务 MAY 选择 重新开始一个 traceparent 字段,以完全消除这些风险。供应商 SHOULD 找到一种方法,尽量减少分布式跟踪 重新开始的次数,以促进跟踪供应商的互操作性。可以使用不同技术 代替重新开始。例如,服务可以定义上游和下游连接的信任边界,以及 任何请求可能带来的暴露级别。例如,供应商可能只针对来自或发往 外部服务的认证请求重新开始 traceparent

服务还可以定义一种算法和审计机制,以验证 traceparent 字段中 传入或传出随机数的随机性。注意,该算法是 服务特定的,并非本规范的一部分。一个示例可能是时间算法,其中 将可逆哈希函数应用于当前时钟时间。接收方可以验证该时间 是否在约定边界内,这意味着该随机数是使用所要求的算法 生成的,并且实际上不包含任何个人身份信息。

6.2 tracestate 字段的隐私

tracestate 字段可以在任何键中包含任意不透明值。该标头的主要目的 是在不同分布式跟踪系统之间提供附加的供应商特定跟踪标识信息。

供应商 MUST NOTtracestate 标头中包含任何个人身份信息。

对个人信息暴露极其敏感的供应商 MAY 实现 对未知键对应值的选择性移除。供应商 SHOULD NOT 改变 tracestate 字段,因为这会破坏允许多个 跟踪系统协作的目的。

6.3 其他风险

当供应商在响应中包含 traceparenttracestate 标头时,这些 值可能会无意中传递给跨源调用方。供应商应确保它们只在响应参与了该跟踪的系统时 包含这些响应标头。

7. 安全考虑事项

与本规范相关的潜在安全风险有两类:信息暴露和 针对供应商的拒绝服务攻击。

依赖 traceparenttracestate 标头的供应商也应遵循所有 解析潜在恶意标头的最佳实践,包括检查标头长度和 标头值的内容。这些实践有助于避免缓冲区溢出和 HTML 注入攻击。

7.1 信息暴露

如隐私一节所述,traceparenttracestate 标头中的信息可能携带可被认为是敏感的信息。例如, traceparent 可能允许将一个请求与另一个请求发送的数据关联起来, 或者 tracestate 标头可能暗示调用方使用的监控软件版本。 这些信息可能被用于构建更大规模的攻击。

应用所有者应确保没有专有或机密信息存储在 tracestate 中,或者应确保发送到外部系统的请求中不存在 tracestate

7.2 拒绝服务

当在具有公共 API 的服务上启用分布式跟踪,并且天真地继续任何设置了 sampled 标志的跟踪时,恶意攻击者可能会用跟踪 开销压垮应用,伪造 trace-id 冲突使监控数据不可用,或者让你的 SaaS 跟踪供应商账单飙升。

跟踪供应商和平台应考虑这些情况,并确保设置制衡措施, 以防止恶意或编写不佳的调用方造成监控拒绝。

这种保护的一个示例可以是对认证请求和未认证请求采用不同的跟踪行为。 也可以实现各种数据记录速率限制器。

7.3 其他风险

应用所有者需要确保测试所有会导致发送 traceparenttracestate 标头的代码路径。例如,在单页浏览器 应用中,进行跨源请求很常见。如果这些代码路径之一导致 traceparenttracestate 标头通过受 Access-Control-Allow-Headers [FETCH] 限制的跨源调用发送,则可能会失败。

8. trace-id 字段生成的考虑事项

本节是非规范性的。

本节建议平台或跟踪供应商在实现 trace-id 生成和传播算法时 考虑一些最佳实践。这些 实践将确保不同系统之间具有更好的互操作性。

8.1 trace-id 的唯一性

trace-id 的值 SHOULD 是全局唯一的。该字段通常用于 唯一标识一个分布式跟踪分布式跟踪常常会跨越各种组件, 例如云服务。云服务往往服务于多种客户端,并且具有非常 高的请求吞吐量。因此,trace-id 的全局唯一性很重要, 即使局部唯一性看起来可能是一个不错的解决方案。

8.2 trace-id 的随机性

随机生成的 trace-idSHOULD 优先于其他 生成全局唯一标识符的算法。trace-id 的随机性 处理了暴露不必要信息所带来的一些安全隐私 顾虑。随机性 还允许跟踪供应商基于 trace-id 字段值作出采样决策, 并避免传播额外的采样上下文。

如果 random-trace-id 标志被设置,则 trace-id 至少最右侧的 7 个字节 MUST 在 区间 [0..2^56-1] 上以均匀分布 随机(或伪随机)选择。

如下一节所示,如果 trace-id 的一部分是非随机的, 则为了与某些现有系统更好地互操作,trace-id 的随机部分尽可能位于 trace-id 的最右侧很重要。

8.3 为内部标识符较短的兼容平台处理 trace-id

有些跟踪系统使用短于 16 字节的 trace-id, 并且仍然愿意采用本规范。

如果这样的系统能够传播完全符合要求的 trace-id,即便 内部用途仍需要较短的、不符合要求的标识符, 鼓励该系统利用 tracestate 标头来传播 附加的内部标识符。然而,如果系统更愿意使用 内部标识符作为完全符合要求的 trace-id 的基础,则它 SHOULD 被合并到 trace-id 的尽可能靠右部分。例如,跟踪 系统可能接收到 234a5bcd543ef3fa53ce929d0e0e4736 作为 trace-id,然而 内部会使用 53ce929d0e0e4736 作为标识符。

8.4 与使用较短标识符的现有系统互操作

有些跟踪系统无法传播完整的 16 字节 trace-id。为了在完全符合要求的 系统与这些现有系统之间实现更好的互操作性,推荐以下实践:

  1. 当系统创建出站消息并需要从较短标识符生成完全 符合要求的 16 字节 trace-id 时,它 SHOULD 在 原始标识符左侧填充零。例如,标识符 53ce929d0e0e4736 SHOULD 被转换为 trace-id000000000000000053ce929d0e0e4736。如果生成的 trace-id 值 不满足 random-trace-id 标志的约束,则该标志 MUST 被设置为 0
  2. 当系统接收传入消息并需要将 16 字节 trace-id 转换为较短标识符时,trace-id 最右侧部分 SHOULD 被用作该标识符。例如,如果传入请求中 trace-id 的值为 234a5bcd543ef3fa53ce929d0e0e4736,则跟踪系统 SHOULD 使用值为 53ce929d0e0e4736 的标识符。

当跟踪系统将其他分布式跟踪上下文传播格式转换为 W3C Trace Context 时,也预期采用类似转换。较短的 标识符 SHOULD 在转换为 16 字节 trace-id 时左侧填充零,并且 trace-id 的最右侧部分 SHOULD 被 用作较短的 标识符。

注意,许多无法传播整个 trace-id 的现有系统也不会传播 tracestate 标头。不过,这样的系统 仍然可以使用 tracestate 标头来传播该系统已知的附加数据。 例如,一些系统使用两个标志来表示 是否需要记录分布式跟踪。在这种情况下,一个标志可以作为 traceparent 标头的 sampled 标志发送,而 tracestate 可用于 发送和接收一个附加标志。符合要求的系统会将该标志连同 所有其他键/值对一起传播。无法 传播 tracestate 的现有系统会截断 tracestate 中的所有附加值, 只传递该标志。

9. span-id 字段生成的考虑事项

本节是非规范性的。

本节建议在实现 span-id 生成算法时 考虑一些实践,以确保 不同系统之间的互操作性。

9.1 span-id 的唯一性

span-id 的值 SHOULD 在一个分布式跟踪内唯一。 如果 span-id 的值在一个分布式跟踪内不唯一, 则分布式跟踪中 span 之间的父子关系 可能会变得模糊。

9.2 span-id 的随机性

span-id 的值 SHOULD 随机生成。 span-id 的随机性 处理了暴露不必要信息所带来的一些安全隐私 顾虑。 随机性还确保在分布式跟踪内具有很高概率(尽管不能保证) 唯一。

A. 致谢

感谢 Adrian Cole、Christoph Neumüller、Daniel Khan、Erika Arnold、Fabian Lange、Matthew Wear、Philippe Le Hegaret、Reiley Yang、Ted Young、Tyler Benson 和 Victor Soares 对这项工作的贡献。

B. 术语表

本节是非规范性的。

分布式跟踪
分布式跟踪是一组事件,它们作为 单个逻辑操作的结果被触发,并跨应用的各种 组件进行整合。分布式跟踪包含 跨越进程、网络和安全边界的事件。 当有人按下按钮在网站上启动某个操作时, 可能会发起一个分布式跟踪——在这个示例中,该 跟踪将表示处理由该按钮 被按下而发起的一连串请求的下游服务之间的调用。
不透明值
不透明值指只能由生成 该值的分布式跟踪参与者理解 或以任何方式处理的值。任何其他参与者都必须将其视为字节 blob。

C. 参考文献

C.1 规范性参考文献

[BAGGAGE]
分布式上下文的传播格式: Baggage。Sergey Kanzhelev; Yuri Shkuro; Daniel Dyla; J. Kalyana Sundaram. W3C. 28 February 2024. W3C Working Draft. URL: https://www.w3.org/TR/baggage/
[BIT-FIELD]
8 位字段。Wikipedia. URL: https://en.wikipedia.org/wiki/Bit_field
[FETCH]
Fetch 标准。Anne van Kesteren. WHATWG. Living Standard. URL: https://fetch.spec.whatwg.org/
[RFC0020]
用于网络 交换的 ASCII 格式。V.G. Cerf. IETF. October 1969. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc20
[RFC2119]
用于 RFC 中表示 要求级别的关键词。S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC5234]
语法规范的扩展 BNF: ABNF。D. Crocker, Ed.; P. Overell. IETF. January 2008. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc5234
[RFC8174]
RFC 2119 关键词中大写与小写的歧义。B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[RFC9110]
HTTP 语义。R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. IETF. June 2022. Internet Standard. URL: https://httpwg.org/specs/rfc9110.html
[trace-context-protocols-registry]
Trace Context 协议 注册表。Sergey Kanzhelev; Philippe Le Hegaret. W3C. 19 November 2019. W3C Working Group Note. URL: https://www.w3.org/TR/trace-context-protocols-registry/