同一张 Trace 中,订单服务与库存服务可能都有名为 lookup 的 Span。光看 Span 名称区分不出哪个服务生成,也无法知道是谁编写的插桩。service.name、instrumentation scope 与 order.id 属于三个不同层级,不应为了在界面上方便查找而全部塞进 Span attributes。

三个不同的归属

位置 本篇取值 生命周期 / 含义
Resource service.name=order-service 或 inventory-service provider 所代表的遥测生产实体,随该 provider 的 Span 共享
InstrumentationScope order.api@1.0 或 order.storage@2.0 获取 tracer 的插桩代码身份,可与其他 scope 共用同一 Resource
Span attributes order.id=request-42 一次操作上的数据,未设置时不会从相邻 Span 自动继承

订单服务的两个 tracer 可以分别代表入口层和存储层,但仍属于同一个 service;另一个 provider 才模拟库存服务。service.name 是资源属性,不是每个请求的业务 ID;scope 名也不是下游服务名。语义约定的版本在工程 VERSIONS.md 中冻结,本篇只要求区别字段所属的协议层,不推断任意后端索引方案。

沿冻结实现找字段来源

公开 SdkTracerProvider.builder().setResource(...) 在创建 provider 时绑定 Resource。Resource.create 从 Attributes 构造资源对象;Resource.merge 处理合并时的字段覆盖,不会自动把资源写进 Span 的请求属性。公共 TracerProvider.get(scope, version) 在 SdkTracerProvider.get 形成 scope 信息;InstrumentationScopeInfo.getName 描述插桩方身份,并不引用请求字段。

SdkSpanBuilder.startSpan 创建 Span 时携带 provider 的 resource 和 tracer 的 scope;一次 setAttribute("order.id", ...) 则只进入该 Span 的属性集合。SpanData 把这三类字段分开公开给测试。上游 SdkTracerProviderTest 检查默认资源和显式资源分支;InstrumentationScopeInfoTest 检查 scope 的构造规则。这是所冻结版本的静态路径;本篇实验另外检查值的实际归属。

两个服务,两个 scope

从 examples/opentelemetry-java/ 运行 ./mvnw -q -pl sdk-labs -Dtest=Lab02Test test。教学测试逐个创建 provider,每个分别使用 order-service 与 inventory-service 的 Resource;每个 provider 取两个 tracer 并结束两个 Span。测试在关闭 provider 前检查:同一 provider 的两个 Span 含相同的资源服务名,但 scope 名和版本不同;order.id 只在 lookup 的 Span attributes 中,第二个 select 没有此属性,且两个 Span 的 attributes 中都没有 service.name。

实际输出:

1
2
LAB02 service=order-service scopes=order.api,order.storage requestAttribute=span-only
LAB02 service=inventory-service scopes=order.api,order.storage requestAttribute=span-only

这组内存数据不说明订单服务真的向库存服务发出 HTTP 请求,也没有传播 traceparent:两个名字在当前仅表示两个本地资源对照。跨服务传播留待 06 篇,不能凭 service.name 相同或不同推断父子关系。

误解与使用边界

将 order.id 设为 Resource 属性,会把一次请求的高变化字段提升为生产实体的身份;将 service.name 只写成 Span attribute,又失去 Resource 维度的语义。字段层级是一种管理契约,但不能替代对标签基数、脱敏和导出结构的约束。另一个误解是把 scope 名当作服务名:同一服务常有多个插桩库,两个概念并不一一对应。

练习一。 在 Lab02Test 的第二个 tracer 上也设置 order.id,检查两条 Span attributes 的差异;再只更改 Resource,确认请求属性不随之改变。

练习二。 在一个 provider 中改为两个不同的 scope version,思考定位插桩升级与定位实例升级各应该观察哪个字段。先预测 SpanData 的改变,再执行断言。

系列导航与资料

00 导读与第一条 Trace · 01 API、SDK 与初始化 · 当前篇:02 Resource 与 InstrumentationScope。下一篇:03 Span 生命周期(待写)。

参考资料:语义约定、上文固定 SHA 的 Java 实现及测试(Apache-2.0)、本系列 Lab02Test(独立教学实验)。