Copyright © 2024 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
本规范定义了标准 HTTP 标头以及一种值格式,用于传播能够支持分布式跟踪场景的上下文信息。 本规范标准化了上下文信息在服务之间发送和修改的方式。上下文信息唯一标识分布式 系统中的各个请求,并且还定义了一种添加和传播提供者特定上下文信息的方法。
本节描述的是本文档在发布时的状态。当前 W3C 发布物列表以及本技术报告的最新修订版可在 https://www.w3.org/TR/ 上的 W3C 技术 报告索引中找到。
这个新版本增加了关于 trace-id 和 span-id
字段生成的考虑事项,并增加了随机 trace ID 标志。
作为退出标准,工作组打算使用测试套件,展示至少 2 个采用并使用本 规范的实现。
本文档由分布式 跟踪工作组作为候选推荐草案,使用 推荐标准轨道发布。
作为候选推荐发布并不 意味着得到 W3C 及其成员的认可。候选 推荐草案整合了工作组 打算纳入后续候选推荐快照的、相对于上一份候选推荐的更改。
本文档是一份草案文档,可能随时被其他 文档更新、替换或废弃。不应将本文档作为 进行中的工作以外的内容引用。
本文档由一个按照 W3C 专利 政策运作的小组制定。 W3C 维护一份 与该小组交付物相关的任何专利 披露的公开列表; 该页面还包括披露专利的说明。实际 知悉某项专利、且该个人认为该专利包含 必要权利要求的个人, 必须按照 W3C 专利政策第 6 节 披露相关信息。
本文档受 2023 年 11 月 03 日 W3C 流程文档约束。
除标记为非规范性的各节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。 本规范中的其他所有内容均为规范性内容。
本文档中的关键词 MAY、MUST、MUST NOT、SHOULD 和 SHOULD NOT 应按 BCP 14 [RFC2119] [RFC8174] 中所述解释,且仅当它们像这里所示那样以全大写形式出现时才如此解释。
分布式跟踪是一种由跟踪工具实现的方法,用于跨多个软件组件跟踪、分析和调试一个 事务。通常,一个分布式跟踪 会穿越多个组件,因此要求它能够在所有参与 系统中被唯一标识。跟踪上下文传播会传递这种唯一标识。如今,跟踪上下文 传播由每个跟踪供应商分别实现。在多供应商环境中,这 会导致互操作性问题,例如:
过去,这些问题并没有产生重大影响,因为大多数应用都由单一 跟踪供应商监控,并且停留在单一平台提供者的边界之内。如今,越来越多的应用 是高度分布式的,并利用多个中间件服务和 云平台。
现代应用的这种转变需要一个分布式跟踪上下文传播标准。
跟踪上下文规范定义了一种通用约定的格式,用于交换跟踪 上下文传播数据——称为跟踪上下文。跟踪上下文通过以下方式解决 上述问题:
用于传播跟踪数据的统一方法提高了对分布式 应用行为的可见性,有助于问题和性能分析。跟踪 上下文提供的互操作性是管理现代基于微服务的应用的前提条件。
Trace Context 规范的当前版本面向应用和服务的实现, 包括在浏览器中运行的 Web 应用。Web 浏览器或用户代理目前 不在目标实现范围内。
跟踪上下文被拆分为两个独立的传播字段,用以支持互操作性和 供应商特定的可扩展性:
traceparent 用一种可移植、固定长度的格式描述传入请求在其跟踪图中的位置。
其设计侧重于快速解析。每个跟踪工具即便只依赖
tracestate 中的供应商特定信息,也 MUST 正确设置 traceparent
tracestate 使用由一组名称/值对表示的供应商特定数据来扩展
traceparent。在 tracestate 中存储信息是可选的。
跟踪工具在与跟踪上下文交互时可以提供两个级别的一致行为:
traceparent 和
tracestate 标头,并保证跟踪不会中断。这种行为也称为
转发一个跟踪。
traceparent 标头以及 tracestate 标头中包含
其专有信息的相关部分来参与一个跟踪。这也称为参与一个跟踪。
跟踪工具可以针对它所监控组件的每个单独请求选择改变这种行为。
本节描述分布式跟踪上下文到
traceparent 和
tracestate HTTP 标头的绑定。
traceparent 请求标头以一种所有供应商都能理解的
通用格式表示跟踪系统中的传入请求。下面是一个 traceparent 标头示例。
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate 请求标头包含一个可能采用供应商特定格式表示的父项:
tracestate: congo=t61rcWkgMzE
例如,假设系统中的客户端和服务器使用不同的跟踪供应商:Congo 和 Rojo。一个 在 Congo 系统中被跟踪的客户端会向一个出站 HTTP 请求添加以下标头。
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: congo=t61rcWkgMzE
注:在这种情况下,tracestate 值 t61rcWkgMzE 是
对父 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 条目应该位于最左侧。
traceparent HTTP 标头字段标识跟踪系统中的传入请求。它
有四个字段:
versiontrace-idparent-idtrace-flags标头名称:traceparent
该标头名称是 ASCII
大小写不敏感的。也就是说,TRACEPARENT、TraceParent 和
traceparent 被认为是同一个标头。该标头名称是单个词;它
不包含任何分隔符,例如连字符。
为了提高跨多种协议的互操作性并鼓励成功集成, 跟踪系统 SHOULD 将标头名称编码为 ASCII 小写。
本节使用 [RFC5234]
的扩展巴科斯-诺尔范式(ABNF)记法,包括该文档中的
DIGIT 规则。DIGIT 规则定义单个数字字符
0-9。
HEXDIGLC = DIGIT / "a" / "b" / "c" / "d" / "e" / "f" ; lowercase hex character
value = version "-" version-format
短横线(-)字符用作字段之间的分隔符。
version = 2HEXDIGLC ; this document assumes version 00. Version ff is forbidden
版本(version)是一个 8 位无符号整数值,序列化为包含两个字符的 ASCII 字符串。
版本 255("ff")无效。本文档指定 traceparent 标头的版本
0("00")。
以下 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.
这是整个跟踪森林的 ID,用于通过一个系统唯一标识一个分布式跟踪。
它表示为一个 16 字节数组,例如
4bf92f3577b34da6a3ce929d0e0e4736。
所有字节均为零(00000000000000000000000000000000)被视为无效
值。
trace-id 的值 SHOULD 是全局唯一的。
一种推荐方法是在足够确定的程度上保证全局唯一性,并处理一些隐私和
安全方面的考虑,即随机(或伪随机)生成 trace-id。
实现者 SHOULD 使用一种 trace-id 生成方法,该方法
至少随机(或伪随机)生成该 ID 最右侧的 7 个字节。
如果最右侧的 7 个字节是随机(或伪随机)生成的,则相应的随机 trace id 标志 SHOULD 被
设置。
有关更多详细信息,参见trace-id 字段生成的
考虑事项。
如果 trace-id 值无效(例如,如果它包含不允许的字符
或全为零),供应商 MUST 忽略 traceparent。
这是调用方所知的此请求的 ID(在某些跟踪系统中,这称为
span-id,其中 span 是一次客户端请求的执行)。它
表示为一个 8 字节数组,例如 00f067aa0ba902b7。所有字节均为零
(0000000000000000)被视为无效值。
当 parent-id 无效(例如,如果它包含非小写十六进制字符)时,
供应商 MUST 忽略 traceparent。
这是一个8 位字段, 用于控制采样、跟踪级别等跟踪标志。这些标志是调用方给出的建议, 而不是必须遵循的严格规则,原因有三点:
你可以在本规范的安全考虑事项一节中找到更多内容。
与其他字段一样,trace-flags 是十六进制编码的。例如,所有 8 个标志
都设置时为 ff,没有设置标志时为 00。
由于这是一个位字段,因此不能通过简单的相等比较来解释这些标志。
例如,01(00000001)和 03
(00000011)都表示该跟踪已被采样,因为 sampled 标志
(00000001)被设置;而 03 和 02(00000010)
都表示 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;
设置时,最低有效位(最右侧)表示调用方可能已记录 跟踪数据。未设置时,调用方没有带外记录跟踪数据。
有许多记录场景可能会破坏分布式跟踪:
由于这些问题,跟踪供应商会作出自己的记录决策,并且对于完成此任务的最佳算法 并没有共识。
各种技术包括:
这些技术的实现方式可以是跟踪供应商特定的,也可以由应用定义。
tracestate 字段被设计用于处理针对给定供应商作出
记录决策(或其他特定信息)的各种技术。
sampled 标志提供了更好的供应商间互操作性。它允许
供应商传达记录决策,并为客户提供更好的体验。
例如,当 SaaS 服务参与一个分布式跟踪时,该服务并不知道
其调用方使用的是哪个跟踪供应商。该服务可能会出于监控或故障排查目的生成
传入请求的记录。sampled 标志可用于确保调用方标记为需要记录的
请求的信息也会被下游的 SaaS 服务记录,以便调用方可以排查
每个已记录请求的行为。
sampled 标志除只能在parent-id 被更新时
被改变外,对其改变没有其他限制。
以下是一组供应商 SHOULD 使用的建议, 用于提高供应商互操作性。
sampled 标志中。sampled 标志值。
安全考虑事项 SHOULD
被应用,以防止该标志被滥用或恶意使用。
sampled 标志应该保持不变地传播。当跟踪由该组件发起时,
它应该默认设置为 0。
还有两个额外选项,供应商 MAY 遵循:
sampled 标志设置为 1 来传达记录优先级。
sampled
标志设置为 1。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 字段生成的考虑事项。
其他标志(例如 00000100)的行为未定义,并保留
供将来使用。供应商 MUST 将这些标志设置为零。
调用方对该请求进行了采样时的有效 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
本规范对跟踪上下文的未来版本持有明确意见。本
规范的当前版本假设 traceparent 标头的未来版本将以增量方式
扩展当前版本。
在解析具有意外格式的标头时,供应商 MUST 遵循这些规则:
透传服务不应分析版本。它们应该预期将来的标头可能 具有更大的大小限制,并且只拒绝过大的标头。
当版本前缀无法解析时(它不是 2 个十六进制字符后跟短横线
(-)),实现应该重新开始跟踪。
如果检测到更高版本,实现 SHOULD 尝试 通过以下方式解析它:
trace-id(从第一个短横线后开始,读取接下来的 32 个字符)。
供应商 MUST 检查这 32 个字符是否为十六进制,并且
后面跟着一个短横线(-)。parent-id(从第 35 个位置的第二个短横线后开始,
读取接下来的 16 个字符)。供应商 MUST 检查这 16 个字符是否为
十六进制,并且后面跟着一个短横线。flags 的 sampled 位(从第三个
短横线后读取 2 个字符)。供应商 MUST 检查这 2 个字符要么位于
字符串末尾,要么后面跟着一个短横线。如果三个值都成功解析,供应商应该使用它们。
供应商 MUST NOT 解析或假定此
版本中的未知字段有任何含义。供应商 MUST 使用这些字段按照
实现所知的最高版本规范(在本规范中为 00)构造新的
traceparent 字段。
tracestate HTTP 标头的主要目的是在不同分布式跟踪系统之间提供附加的
供应商特定跟踪标识信息,并且它是 traceparent 字段的配套标头。
它还传达请求在多个分布式跟踪图中的位置信息。
如果供应商未能解析 traceparent,则它 MUST NOT 尝试
解析 tracestate。注意,反过来并不成立:解析
tracestate 失败 MUST NOT 影响
traceparent 的解析。
tracestate HTTP 标头 MUST NOT 用于任何
不是由跟踪系统定义的属性。[BAGGAGE] MAY 可用于定义和传播此类应用级属性。
标头名称:tracestate
该标头名称是 ASCII
大小写不敏感的。也就是说,TRACESTATE、TraceState 和
tracestate 被认为是同一个标头。该标头名称是单个词,它
不包含任何分隔符,例如连字符。
为了提高跨多种协议的互操作性并鼓励成功集成, 跟踪系统 SHOULD 将标头名称编码为 ASCII 小写。
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-members 的
list。list-member 是由等号
(=)分隔的键/值对。围绕 list-member 的空格和水平制表符会被忽略。
一个 list 中最多可以有 32 个 list-member。如果添加条目
会导致 tracestate 列表包含超过 32 个 list-members,则
应从列表中移除最右侧的 list-member。
允许空的以及仅包含空白的列表成员。供应商 MUST 接受
空的 tracestate 标头,但 SHOULD 避免发送它们。
tracestate 中允许空列表成员,是因为当发送多个
tracestate 标头时,供应商很难识别空值。出于类似原因,
也允许空白字符,因为某些供应商会在逗号分隔符后自动注入空白,
即使在空标头的情况下也是如此。
一个包含两个 list-member 的 list 的简单示例可能如下所示:
vendorname1=opaqueValue1,vendorname2=opaqueValue2。
list = list-member 0*31( OWS "," OWS list-member )
list-member = (key "=" value) / OWS
list 的标识符是较短的(最多 256 个字符)文本标识符。
list-member 包含一个键/值对。
键是描述供应商的标识符。
key = ( lcalpha / DIGIT ) 0*255 ( keychar )
keychar = lcalpha / DIGIT / "_" / "-"/ "*" / "/" / "@"
lcalpha = %x61-7A ; a-z
key MUST 以小写字母或数字开头,并
最多包含 256 个字符,包括小写字母(a-z)、
数字(0-9)、下划线(_)、短横线
(-)、星号(*)、正斜杠(/)和 at 符号
(@)。
值是一个不透明字符串,包含最多 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
tracestate 值是跟踪图键/值对的串联。
示例:vendorname1=opaqueValue1,vendorname2=opaqueValue2
每个键只允许一个条目。例如,如果供应商名称为 Congo,且跟踪在
其系统中开始,随后经过名为 Rojo 的系统,之后又返回到 Congo,则
tracestate 值不会是:
congo=congosFirstPosition,rojo=rojosFirstPosition,congo=congosSecondPosition
相反,该条目会被重写为仅包含最近的位置:
congo=congosSecondPosition,rojo=rojosFirstPosition
详情参见改变 tracestate 字段。
供应商 SHOULD 至少传播组合标头的 512 个字符。
该长度包括分隔列表项所需的逗号和可选空白
(OWS)字符。
在某些系统中,传播 512 个字符的 tracestate 可能
代价高昂。在这种情况下,所传播 tracestate 标头的最大大小 SHOULD 被记录并解释。传播
tracestate 的成本 SHOULD 与为最终用户启用的
监控场景的价值进行权衡。
在由于标头值总大小而截断 tracestate 的情况下,
供应商 MUST 截断整个条目。长度超过
128 个字符的条目 SHOULD 首先被移除。然后条目
SHOULD 从 tracestate 末尾开始移除。
其他截断策略,例如安全列表条目、阻止列表条目或基于大小的
截断 SHOULD NOT 被使用。
单个跟踪系统(通用格式):
tracestate: rojo=00f067aa0ba902b7
多个跟踪系统(具有不同格式):
tracestate: rojo=00f067aa0ba902b7,congo=t61rcWkgMzE
tracestate 的版本由 traceparent 标头的版本前缀定义。
如果检测到更高版本,供应商需要尽其所能尝试解析 tracestate。
是否使用部分解析得到的
tracestate 键/值对由供应商决定。
接收到没有 traceparent 标头的请求的供应商 SHOULD
为出站请求生成 traceparent 标头,从而有效地开始一个新的跟踪。
不这样做的一个可能原因可能是在性能敏感场景中,供应商决定
不采样某个请求。注意,对于大多数场景,即使不采样,也期望供应商生成该标头,
以便将采样决策传播到下游。
接收到 traceparent 请求标头的供应商 MUST 将其发送到
出站请求。它 MAY 在传递到
出站请求之前改变该标头的值。
如果 traceparent 字段的值在传播前未被更改,
tracestate MUST NOT 也被修改。未修改的标头
传播通常在代理等透传服务中实现。这种行为也可以在当前不收集
分布式跟踪信息的服务中实现。
以下是允许的改变列表:
parent-id:parent-id
字段的值可以设置为表示当前操作 ID 的新值。这是
最典型的改变,应被视为默认改变。sampled:sampled
字段的值反映调用方的记录行为:要么跟踪数据被丢弃,要么可能已
被带外记录。这可以通过双向切换该标志来表示。该
改变向下游供应商提供有关其父项信息被记录可能性的
信息。更新 sampled 标志时,parent-id 字段 MUST
被设置为新值。trace-id、parent-id、
trace-flags)都会重新生成。该改变用于被定义为安全网络入口前门的服务,
并消除潜在的拒绝服务攻击面。供应商
SHOULD 在 traceparent 重新开始时清理
tracestate 集合。在少数情况下,重新开始后必须保留原始
tracestate 条目。这通常发生在跟踪流的某个点上
trace-id 被恢复回来时,例如,当它离开安全网络时。
然而,这 SHOULD 是一个明确的决定,而不是默认行为。
00)定义了供应商在接收到更高版本的
traceparent 标头时的行为。在这种情况下,第一个改变是降级该
标头的版本。其他改变允许与此改变结合使用。
供应商 MUST NOT 对 traceparent
标头作出任何其他改变。
接收到 tracestate 请求标头的供应商 MUST 将其发送到
出站请求。它 MAY 在传递到
出站请求之前改变该标头的值。改变 tracestate 时,未修改键/值对的顺序 MUST 被保留。修改后的键 MUST 被移动到列表的
开头(左侧)。
以下是允许的改变:
tracestate 键。第二种场景是截断较长的
tracestate。最后,供应商 MAY 也可以丢弃
不是由它们生成的重复键。
本节是非规范性的。
本节提供了一个逐步示例,说明跟踪供应商如何接收带有跟踪上下文 标头的请求,处理该请求,然后可能将其转发。该描述可在实现符合 跟踪上下文的跟踪系统、中间件(例如代理或消息 总线)或云服务时用作参考。
该处理模型描述了修改并转发跟踪上下文
标头的供应商的行为。该模型如何工作取决于是否接收到 traceparent 标头。
如果未收到 traceparent 标头:
traceparent 和
tracestate 标头。
traceparent 标头,供应商会创建一个新的
trace-id 和 parent-id,用于表示当前请求。(注:如果
供应商不采样该请求,并希望通过 sampled 标志将该采样决策
传达给下游,则供应商 MAY 创建不与任何实际跟踪
数据相关联的 trace-id 和 parent-id。供应商 MAY
也可以决定不向下游传达该采样
决策。)
traceparent
标头的 tracestate 标头,则它无效,并且 MUST 被丢弃。tracestate 标头并添加一个
新的键/值对。traceparent 和 tracestate 标头。如果收到 traceparent 标头:
traceparent 和
tracestate 标头。
traceparent 标头存在,供应商会尝试解析
traceparent 标头的版本。traceparent 标头并删除 tracestate。
00)来解析
trace-id 和 parent-id。
供应商只会解析本规范此版本支持的 trace-flags 值,
并忽略所有其他值。如果解析失败,供应商会创建
新的 traceparent 标头并删除 tracestate。供应商
将在出站请求中把所有未解析/未知的 trace-flags 设置为 0。
trace-id
和 parent-id。如果 trace-id、parent-id 或
trace-flags 中任一无效,供应商会创建新的 traceparent
标头并删除 tracestate。
tracestate 标头。如果
tracestate 标头无法解析,供应商 MAY 丢弃
整个标头。无效的 tracestate 条目 MAY 也可以
被丢弃。
供应商 MUST 修改 traceparent 标头:
parent-id:属性
parent-id 的值 MUST 被设置为
表示当前操作 ID 的值。
sampled:sampled 的值
反映调用方的记录行为。如果跟踪数据可能被记录,
trace-flags 的 sampled 标志值 MAY
被设置为
1,否则设置为 0。
设置该标志并不保证跟踪会被记录,但
会提高端到端记录跟踪的可能性。
供应商 MAY 修改 tracestate 标头:
供应商为出站请求设置 traceparent 和 tracestate 标头。
上述处理模型描述了处理跟踪上下文标头的一整套步骤。
但是,在某些情况下,供应商可能只支持上述步骤的一个子集。
代理或消息中间件 MAY 决定不修改
traceparent 标头,而是移除无效标头或向
tracestate 添加附加信息。
虽然跟踪上下文是为 HTTP 定义的,但作者承认它也与其他通信 协议相关。本规范的扩展以及外部组织制定的规范 定义了其他协议中跟踪上下文序列化和反序列化的格式。注意,这些 扩展的成熟度级别可能与本规范不同。
请参阅 [trace-context-protocols-registry],了解 其他协议的跟踪上下文实现详情。
将标头传播到下游服务以及存储这些标头的值的要求,会带来
潜在的隐私顾虑。跟踪供应商 MUST NOT 将 traceparent
和 tracestate 字段用于任何个人身份信息或其他敏感信息。
这些字段的唯一目的是启用跟踪关联。
供应商 MUST 评估标头滥用的风险。本节提供一些 考虑事项以及对存储和传播这些标头相关风险的初步评估。 跟踪供应商可以选择在允许跟踪系统执行可能传播或存储这些字段的代码之前, 检查并移除字段中的敏感信息。不过,所有改变都应 符合本规范中定义的改变列表。
traceparent 字段 MUST NOT 包含任何个人身份
信息。实现这一点的一种方式是使用不会暴露任何个人身份信息的随机数
生成器来随机生成所有 trace ID。任何用于生成 trace ID 的随机数生成器
MUST NOT 依赖任何可能具有个人身份属性的信息作为输入或种子
状态。
traceparent 字段的另一个隐私风险,是能够关联作为
单个事务一部分发出的请求。下游服务可能会跟踪并关联
单个事务中发出的两个或更多请求,并可能基于另一个请求中的
信息对某个请求调用方的身份作出推断。
注意,traceparent 字段的这些隐私顾虑是理论性的,而非
实际性的。某些发起或接收请求的服务 MAY 选择
重新开始一个 traceparent 字段,以完全消除这些风险。供应商 SHOULD 找到一种方法,尽量减少分布式跟踪
重新开始的次数,以促进跟踪供应商的互操作性。可以使用不同技术
代替重新开始。例如,服务可以定义上游和下游连接的信任边界,以及
任何请求可能带来的暴露级别。例如,供应商可能只针对来自或发往
外部服务的认证请求重新开始
traceparent。
服务还可以定义一种算法和审计机制,以验证 traceparent 字段中
传入或传出随机数的随机性。注意,该算法是
服务特定的,并非本规范的一部分。一个示例可能是时间算法,其中
将可逆哈希函数应用于当前时钟时间。接收方可以验证该时间
是否在约定边界内,这意味着该随机数是使用所要求的算法
生成的,并且实际上不包含任何个人身份信息。
tracestate 字段可以在任何键中包含任意不透明值。该标头的主要目的
是在不同分布式跟踪系统之间提供附加的供应商特定跟踪标识信息。
供应商 MUST NOT 在
tracestate 标头中包含任何个人身份信息。
对个人信息暴露极其敏感的供应商 MAY 实现
对未知键对应值的选择性移除。供应商 SHOULD
NOT 改变 tracestate 字段,因为这会破坏允许多个
跟踪系统协作的目的。
当供应商在响应中包含 traceparent 和 tracestate 标头时,这些
值可能会无意中传递给跨源调用方。供应商应确保它们只在响应参与了该跟踪的系统时
包含这些响应标头。
与本规范相关的潜在安全风险有两类:信息暴露和 针对供应商的拒绝服务攻击。
依赖 traceparent 和 tracestate 标头的供应商也应遵循所有
解析潜在恶意标头的最佳实践,包括检查标头长度和
标头值的内容。这些实践有助于避免缓冲区溢出和 HTML 注入攻击。
如隐私一节所述,traceparent 和
tracestate 标头中的信息可能携带可被认为是敏感的信息。例如,
traceparent 可能允许将一个请求与另一个请求发送的数据关联起来,
或者 tracestate 标头可能暗示调用方使用的监控软件版本。
这些信息可能被用于构建更大规模的攻击。
应用所有者应确保没有专有或机密信息存储在
tracestate 中,或者应确保发送到外部系统的请求中不存在
tracestate。
当在具有公共 API 的服务上启用分布式跟踪,并且天真地继续任何设置了
sampled 标志的跟踪时,恶意攻击者可能会用跟踪
开销压垮应用,伪造 trace-id 冲突使监控数据不可用,或者让你的
SaaS 跟踪供应商账单飙升。
跟踪供应商和平台应考虑这些情况,并确保设置制衡措施, 以防止恶意或编写不佳的调用方造成监控拒绝。
这种保护的一个示例可以是对认证请求和未认证请求采用不同的跟踪行为。 也可以实现各种数据记录速率限制器。
应用所有者需要确保测试所有会导致发送
traceparent 和 tracestate 标头的代码路径。例如,在单页浏览器
应用中,进行跨源请求很常见。如果这些代码路径之一导致
traceparent 和 tracestate 标头通过受
Access-Control-Allow-Headers
[FETCH] 限制的跨源调用发送,则可能会失败。
本节是非规范性的。
本节建议平台或跟踪供应商在实现 trace-id 生成和传播算法时
考虑一些最佳实践。这些
实践将确保不同系统之间具有更好的互操作性。
trace-id 的值 SHOULD 是全局唯一的。该字段通常用于
唯一标识一个分布式跟踪。分布式跟踪常常会跨越各种组件,
例如云服务。云服务往往服务于多种客户端,并且具有非常
高的请求吞吐量。因此,trace-id 的全局唯一性很重要,
即使局部唯一性看起来可能是一个不错的解决方案。
随机生成的 trace-id 值 SHOULD 优先于其他
生成全局唯一标识符的算法。trace-id 的随机性
处理了暴露不必要信息所带来的一些安全和隐私
顾虑。随机性
还允许跟踪供应商基于 trace-id 字段值作出采样决策,
并避免传播额外的采样上下文。
如果 random-trace-id 标志被设置,则
trace-id 至少最右侧的 7 个字节 MUST 在
区间 [0..2^56-1] 上以均匀分布
随机(或伪随机)选择。
如下一节所示,如果 trace-id 的一部分是非随机的,
则为了与某些现有系统更好地互操作,trace-id 的随机部分尽可能位于
trace-id 的最右侧很重要。
有些跟踪系统使用短于 16 字节的 trace-id,
并且仍然愿意采用本规范。
如果这样的系统能够传播完全符合要求的 trace-id,即便
内部用途仍需要较短的、不符合要求的标识符,
鼓励该系统利用 tracestate 标头来传播
附加的内部标识符。然而,如果系统更愿意使用
内部标识符作为完全符合要求的 trace-id 的基础,则它 SHOULD
被合并到 trace-id 的尽可能靠右部分。例如,跟踪
系统可能接收到 234a5bcd543ef3fa53ce929d0e0e4736 作为 trace-id,然而
内部会使用 53ce929d0e0e4736 作为标识符。
有些跟踪系统无法传播完整的 16
字节 trace-id。为了在完全符合要求的
系统与这些现有系统之间实现更好的互操作性,推荐以下实践:
trace-id 时,它 SHOULD 在
原始标识符左侧填充零。例如,标识符
53ce929d0e0e4736 SHOULD 被转换为 trace-id
值
000000000000000053ce929d0e0e4736。如果生成的 trace-id 值
不满足 random-trace-id 标志的约束,则该标志 MUST
被设置为 0。
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 中的所有附加值,
只传递该标志。
本节是非规范性的。
本节建议在实现 span-id 生成算法时
考虑一些实践,以确保
不同系统之间的互操作性。
span-id 的值 SHOULD 在一个分布式跟踪内唯一。
如果 span-id 的值在一个分布式跟踪内不唯一,
则分布式跟踪中 span 之间的父子关系
可能会变得模糊。
span-id 的值 SHOULD 随机生成。
span-id 的随机性
处理了暴露不必要信息所带来的一些安全和隐私
顾虑。
随机性还确保在分布式跟踪内具有很高概率(尽管不能保证)
唯一。
感谢 Adrian Cole、Christoph Neumüller、Daniel Khan、Erika Arnold、Fabian Lange、Matthew Wear、Philippe Le Hegaret、Reiley Yang、Ted Young、Tyler Benson 和 Victor Soares 对这项工作的贡献。
本节是非规范性的。
Referenced in: