traceparent 头里到底有什么?一次调用怎么在几个服务之间串成一棵树?中间一跳忘了传会怎样?baggage 和 tracestate 怎么传?收到格式不对的头 SDK 怎么办?这篇用 OpenTelemetry Python SDK 和三个真实的 HTTP 服务逐项试出来。
服务 | span 名 | 类型 | trace_id | span_id | parent_span_id ---+----------+--------+-----------+------------------+----------------- A | A handle | SERVER | 0f2cf66d… | 66a3413344054327 | (根) A | A call | CLIENT | 0f2cf66d… | 7baa12638f1c8308 | 66a3413344054327 B | B handle | SERVER | 0f2cf66d… | 7a9eee88cdffcdac | 7baa12638f1c8308 B | B call | CLIENT | 0f2cf66d… | dc2d8129d88e93d7 | 7a9eee88cdffcdac C | C handle | SERVER | 0f2cf66d… | b43d3b8a9c3667b1 | dc2d8129d88e93d7 这次调用里不同的 trace_id 个数:1 服务 | 收到的 traceparent | 收到的 X-Request-Id ---+---------------------------------------------------------+----------------- A | (无) | req-123 B | 00-0f2cf66d652cac053fbad118b2ab6ff0-7baa12638f1c8308-03 | req-123 C | 00-0f2cf66d652cac053fbad118b2ab6ff0-dc2d8129d88e93d7-03 | req-123 C 收到的 traceparent 的四段:version=00,trace-id=0f2cf66d652cac053fbad118b2ab6ff0(32 个十六进制字符),parent-id=dc2d8129d88e93d7(16 个),trace-flags=03。A 没有收到 traceparent,B 和 C 收到的 trace-id 相同、parent-id 不同:parent-id 是上一跳发出请求的那个 CLIENT span 的 span_id C 收到的 parent-id 对应的 span:B 的 “B call”
trace-id 一路不变,每一跳的 parent-id 是上一跳 CLIENT span 的 span_id;这个 SDK 版本的 trace-flags 是 03。
情形 | trace 关系 | C 的 span | C 收到的 X-Request-Id ---------------------------+--------------------+------------------+------------------- (a)B 不传 traceparent,传请求 ID | A 与 C 是两个不同的 trace | C 的 span 是根 | req-777 (b)B 传 traceparent,不传请求 ID | A 与 C 同一个 trace | C 的 span 有父 span | (没有) (c)B 两个都不传 | A 与 C 是两个不同的 trace | C 的 span 是根 | (没有) 上下文只通过请求头传播:中间一跳没把 traceparent 带出去,下游就从一个新的 trace 开始,链路在这里断开;请求 ID 头同样要每一跳自己转发,SDK 不会替你转
上下文只通过请求头传播,中间一跳不转发,下游就另起一个 trace;请求 ID 头也要每一跳自己转发。
(a)上下文只作为 context 参数传入: 服务 | traceparent | tracestate | baggage | 三个头的字符数 ---+---------------------------------------------------------+--------------------------+------------+-------- A | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 | vendor1=opaque,vendor2=x | user=alice | 89 B | 00-4bf92f3577b34da6a3ce929d0e0e4736-546d58d166c3f735-01 | vendor1=opaque,vendor2=x | (无) | 79 C | 00-4bf92f3577b34da6a3ce929d0e0e4736-cd9479ca94faeb09-01 | vendor1=opaque,vendor2=x | (无) | 79 (b)先 attach 提取出来的上下文: 服务 | traceparent | tracestate | baggage | 三个头的字符数 ---+---------------------------------------------------------+--------------------------+------------+-------- A | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 | vendor1=opaque,vendor2=x | user=alice | 89 B | 00-4bf92f3577b34da6a3ce929d0e0e4736-47dc25bb1f754e39-01 | vendor1=opaque,vendor2=x | user=alice | 89 C | 00-4bf92f3577b34da6a3ce929d0e0e4736-2bec85dc504703d2-01 | vendor1=opaque,vendor2=x | user=alice | 89 tracestate 跟着 span 上下文走,baggage 是独立的上下文内容:只把提取出来的上下文当作 start_as_current_span 的父上下文传进去,新 span 的父子关系是对的,但是当前上下文里没有 baggage,发给下游的请求头里就没有它;要先 attach 才行
tracestate 跟着 span 上下文走;baggage 在当前上下文里,只把提取出的上下文传给 start_as_current_span 不够,要先 attach。
请求带的 traceparent | 值(截取) | SDK 的处理
-----------------+-------------------------------------------------+------------------------------------
合法,已采样(flags=01) | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b… | 延续了这个 trace(parent 是请求里的 parent-id)
合法,未采样(flags=00) | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b | 没有导出任何 span(未采样,span 没有被记录)
十六进制用了大写 | 00-4BF92F3577B34DA6A3CE929D0E0E4736-00F067AA0B… | 另起新 trace(根 span)
trace-id 全 0 | 00-00000000000000000000000000000000-00f067aa0b… | 另起新 trace(根 span)
parent-id 全 0 | 00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000… | 另起新 trace(根 span)
version=ff(非法) | ff-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b… | 另起新 trace(根 span)
version=01 且多出字段 | 01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b… | 延续了这个 trace(parent 是请求里的 parent-id)
缺 trace-flags | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b… | 另起新 trace(根 span)
trace-id 太短 | 00-4bf92f3577b34da6-00f067aa0ba902b7-01 | 另起新 trace(根 span)
尾部有多余字符 | 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0b… | 另起新 trace(根 span)
随便一个字符串 | hello | 另起新 trace(根 span)
格式不对的 traceparent 会被丢掉,服务从一个新的 trace 开始,不会报错;“未采样”标志(flags=00)会让默认采样器也不记录这条 span
ERRORS {}格式不对的 traceparent 被丢掉,服务另起新 trace,不报错;flags=00 让默认采样器不记录 span。