父Span持续100个时间单位,两个直接子Span分别占用[10,60]和[40,80]。如果用100减50再减40,会得到10;可重叠区间[40,60]被减了两遍。用子区间并集计算,未覆盖部分其实是30。
这些是手工构造的时间单位,不是任何服务的延迟实测。例子要说明的是区间算法,而不是哪个模型能在几秒内发现慢RPC。
封面只作并行重叠的概念表现,不按时间比例绘制;精确区间见内图与下面代码。
先固定“自耗时”的定义
本文的定义是:父Span时间窗口减去其直接子Span在父窗口内的区间并集。它是Trace层面的未覆盖墙钟时间,不等于线程CPU时间。异步任务、缺失埋点、采样和跨进程时钟偏差都会影响解释,不能把剩余时间一律归给本地代码。
Spring AI与OpenTelemetry的观测资料提供事件、Span和模型操作的记录约定;从这些记录到根因,仍然需要确定性的清洗和业务核对。Spring AI观测、GenAI指标规范仓库均于2026年10月23日核对相关范围,未据此假定每条Trace都完整。
完整的区间计算
import java.util.ArrayList; import java.util.Comparator; import java.util.List; public class SpanUnion { static long uncovered(long start, long end, List<long[]> children) { if (start > end) throw new IllegalArgumentException("parent"); List<long[]> clipped = new ArrayList<>(); for (long[] c : children) { if (c.length != 2 || c[0] > c[1]) throw new IllegalArgumentException("child"); long a=Math.max(start,c[0]), b=Math.min(end,c[1]); if (a < b) clipped.add(new long[]{a,b}); } clipped.sort(Comparator.comparingLong(a -> a[0])); long covered=0, left=0, right=0; boolean open=false; for (long[] c : clipped) { if (!open) { left=c[0]; right=c[1]; open=true; } else if (c[0] <= right) right=Math.max(right,c[1]); else { covered=Math.addExact(covered, Math.subtractExact(right,left)); left=c[0]; right=c[1]; } } if (open) covered=Math.addExact(covered, Math.subtractExact(right,left)); return Math.subtractExact(Math.subtractExact(end,start),covered); } public static void main(String[] args) { assert uncovered(0,100,List.of(new long[]{10,60},new long[]{40,80}))==30; assert uncovered(0,100,List.of(new long[]{-10,10},new long[]{90,120}))==80; assert uncovered(0,100,List.of())==100; assert uncovered(0,100,List.of(new long[]{0,100}))==0; assert uncovered(0,100,List.of(new long[]{10,20},new long[]{10,20}))==90; System.out.println("span union: 5 hand-checkable cases passed"); } }保存为SpanUnion.java,运行javac --release 11 SpanUnion.java && java -ea SpanUnion。代码使用半开区间,重复区间不重复扣除,越界子区间先裁剪。Math.*Exact让极端时间值溢出时显式失败。输入若为纳秒、微秒或毫秒,必须在同一时间坐标与单位下计算。
算完以后再让模型解释
可以把确定性结果、Span ID、服务名和待核疑点交给模型,但不要让它补出没有采集到的源码行。若多个重复RPC占时较长,它可以提出“N+1调用”候选;确认仍要看调用输入、调用位置和业务必要性。
缺失子Span时,本算法会高估未覆盖时间。跨进程子Span越界时,裁剪只是防止计算出现荒谬负值,不是修复了时钟。因此输出还应保留缺失和越界计数。本次只跑合成区间,没有用真实SkyWalking或OpenTelemetry数据作根因评测。
本地验证记录:Oracle JDK 12.0.1,javac --release 11与java -ea,运行成功,输出span union: 5 hand-checkable cases passed。仅覆盖本例的离线输入;框架、供应商和生产集成仍未测试。