endpoint stat 查询指定 SpanName 无数据
0x01 关键信息
a. 现象
- 在 BKOP 的 endpoint stat 页面中,按指定
service_name和span_name查询时无统计结果。 - 同一组过滤条件在 BKTE 原始 Span 查询中可以查到数据,说明不是链路原始数据缺失。
b. 环境
- BKOP 侧排查 Trace:
app_name=bkmonitor_production,trace_id=5b2af7e688f5f557c227f0f83f28685a。 - BKTE 侧原始数据样例:
app_name=v3_dev,service_name=osm,span_name=services.osm.ticket.views.TicketViewSet.access_business。 - BKTE 侧验证时间窗:
2026-04-14 23:54:14到2026-04-15 23:54:14, 以及 BKOP Trace 内部实际使用的2026-04-14 21:24:09到2026-04-15 21:24:09。 - 受影响样例:
- 应用:
v3_dev - 服务:
osm - SpanName:
services.osm.ticket.views.TicketViewSet.access_business
- 应用:
c. 结论
- endpoint stat 当前实现默认只统计带特定“请求内容”字段的 Span。
- 给定应用中的这批 HTTP server span 没有这些旧字段,因此在聚合前就被过滤掉了。
- 该问题和 OpenTelemetry 新旧 HTTP 语义约定切换有关,
http.url已经不再是稳定依赖字段。
0x02 endpoint stat 当前逻辑
- 入口在
packages/apm_web/meta/resources.py的QueryEndpointStatisticsResource。 - 默认
span_keys固定为以下 5 个字段:db.systemhttp.urlmessaging.systemrpc.systemtrpc.callee_method
perform_request()会把这些字段转换成group_keys, 再调用api.apm_api.query_span(...)做聚合查询。- API 侧
apm/resources.py中的QuerySpanResource会在带group_keys时转调trace_datasource.query_span_with_group_keys(...)。 apm/models/datasource.py中的query_span_with_group_keys()会先构造:group_by:按上面 5 个字段聚合。or_query:要求这 5 个字段里至少有一个exists。
- 聚合结果回到
QueryEndpointStatisticsResource后, 还会继续做一次筛选: 如果当前 bucket 的key中没有非空值, 直接continue丢弃。 - 对 HTTP 类结果,代码还会额外把
http_url拿去匹配 URI 规则,做一次归类展示。
0x03 该问题出现的原因
a. 给定应用的 Span 数据形态不同
- 在 BKTE 原始 Span 查询中,
services.osm.ticket.views.TicketViewSet.access_business这类 Span 可以稳定查到。 - 这些 Span 的关键属性主要是:
attributes.http.routeattributes.http.targetresource.service.namespan_name
- 这批 Span 不带以下字段:
attributes.http.urlattributes.db.systemattributes.messaging.systemattributes.rpc.systemattributes.trpc.*
b. endpoint stat 的预过滤过窄
- 由于
query_span_with_group_keys()的or_query只认旧的 5 类分组字段, 这批 HTTP server span 在进入聚合前就全部被过滤掉。 - 因此后续 ES / UnifyQuery 聚合返回
hits_length=0, 页面上看到的就是“指定 SpanName 的 stat 无数据”。
c. OpenTelemetry 语义约定已变化
- OpenTelemetry 当前规范中,
http.url已标记为 Deprecated, 推荐改用url.full。 - 对 HTTP server span, 规范更推荐使用低基数的
http.route表达路由模板,而不是依赖完整 URL。 - 当前 BKOP endpoint stat 仍以
http.url作为 HTTP 聚合主字段, 与给定应用的上报数据以及新语义约定已经脱节。
0x04 建议的修复方案
a. 调整 HTTP 聚合主字段
- 将 HTTP 场景的主分组字段从
http.url调整为http.route。 http.route不存在时, 再按兼容顺序兜底:url.pathhttp.target
b. 做新旧 semconv 兼容
- 对外展示和内部查询同时兼容以下字段:
- 新字段:
http.route、url.path、url.full - 旧字段:
http.target、http.url
- 新字段:
- 兼容策略应区分“展示字段”和“聚合字段”:
- 聚合优先低基数字段。
- 高基数且可能带敏感参数的
url.full只建议作为排查辅助,不建议直接做 stat 分组。
c. 放宽当前预过滤
- 不要把“必须存在固定分组字段之一”作为聚合前置条件。
- 更稳妥的做法有两种:
- 按节点类别选择不同的候选分组字段。
- 允许 bucket 全空时兜底回落到
span_name或推导出的 HTTP 路由摘要,而不是直接丢弃。
d. 一并修正现有实现细节
trpc_callee_method的 exists 过滤当前检查的是attributes.trpc.namespace, 和分组字段attributes.trpc.callee_method本身不一致,建议顺手修正。- HTTP URL 归类逻辑当前也建立在
http_url之上, 需要同步评估是否改为基于http.route或兼容url.path/http.target。
0x05 参考
- OpenTelemetry HTTP 属性注册表
- OpenTelemetry HTTP spans 语义约定
packages/apm_web/meta/resources.pyapm/resources.pyapm/models/datasource.py