trace 的瀑布图为什么会出现子 span 比父 span 先开始?为什么一个请求的耗时在不同地方不一样?为什么多个请求被并进同一个 trace?丢一点 span 会让多少 trace 残缺?这篇用 OpenTelemetry Python SDK 逐项复现。
下游时钟相对调用方 | 请求数 | 下游 span 比父 span 先开始 | 下游 span 比父 span 后结束 | 表观网络耗时中位数(ms,下游开始 − 调用方开始) ----------+-----+---------------------+---------------------+--------------------------- 0(同步) | 100 | 0 | 0 | 0.93 +5 ms | 100 | 0 | 100 | 5.97 +20 ms | 100 | 0 | 100 | 20.36 -5 ms | 100 | 99 | 0 | -4.66 -20 ms | 100 | 99 | 0 | -18.96 +100 ms | 100 | 0 | 100 | 100.45 下游时钟偏快时,子 span 看起来在父 span 开始很久之后才开始、甚至在父 span 结束之后才结束;偏慢时子 span 看起来比父 span 先开始。追踪后端画出来的瀑布图、算出来的网络耗时都会受影响;父子关系本身(parent_id)不受时钟影响
父子关系由 parent_id 决定,不受时钟影响,但时间轴上的位置、表观网络耗时都会错位。
做法 | 本该是根 span、却有父 span 的请求数 | 6 个请求一共出现的 trace 数 -------------+-------------------------+------------------- (a)正确 detach | 0 / 6 | 6 (b)忘记 detach | 5 / 6 | 1 线程池里的线程会被复用,没有 detach 的上下文留在线程里,后面的请求创建 span 时以它为父:每个请求的根 span 被挂在了上一个请求的 span 下面,6 个互不相干的请求并成了一个 trace
线程池线程复用,没 detach 的上下文留在线程里,后面的请求都挂到它下面。
request span:15.2 ms;background span(是 request 的子 span:True):207.4 ms;background 比 request 晚结束 192.4 ms。按 trace 里“根 span 的耗时”统计这次请求耗时是 15.2 ms,按“trace 里最晚的 span 结束时间 − 最早的开始时间”统计是 207.6 ms,两者之比 13.6 倍
后台任务比父 span 活得久,根 span 的耗时和整个 trace 的跨度差很多。
每个 span 被丢弃的概率 | 完整的 trace | 有孤儿 span 的 trace(父 span 丢了) | 不完整但没有孤儿(只丢了叶子) | 一个 span 都没收到
---------------+-----------+-----------------------------+-----------------+-------------
0% | 2000 | 0 | 0 | 0
1% | 1893 | 66 | 41 | 0
5% | 1529 | 289 | 182 | 0
20% | 634 | 886 | 479 | 1
丢掉任何一个非叶子 span,它的子 span 就成了孤儿,追踪后端要么把它们当作新的根,要么显示“缺失的父 span”;丢的概率是按 span 算的,一个 trace 有 5 个 span,完整的 trace 比例比 1 减去丢弃概率低得多
ERRORS {}丢掉非叶子 span 会产生孤儿 span;完整的 trace 比例按 span 数的幂次下降。