2. 核心概念:Span、Span Attributes、Resource、Resource Attributes、Instrumentation Scope
在 OpenTelemetry 的数据模型中,Span、Resource 和 Instrumentation Scope 是构成可观测性信号的三大支柱。理解它们各自的职责以及属性(Attributes)的注入位置,是掌握元数据管理的基础。本章将逐一拆解这些核心概念,并重点说明属性与资源元数据如何在链路中发挥作用。
2.1 Span:分布式追踪的基本单元
Span 代表一个工作单元,是分布式追踪中最小的可观测性记录。每个 Span 记录了一次操作(如 HTTP 请求、数据库查询、函数调用)的开始时间、结束时间、状态以及一组描述该操作的元数据。
- Span 名称:描述操作的人类可读名称,例如
GET /api/users。 - Span 上下文:包含 Trace ID、Span ID 和 Trace Flags,用于在分布式系统中关联父子 Span。
- Span 属性:键值对形式的元数据,用于丰富 Span 的语义信息。
- Span 事件:在 Span 生命周期内发生的带时间戳的日志事件。
- Span 状态:表示操作成功或失败(Unset、Ok、Error)。
http.method、http.status_code 和 user.id,可以快速定位特定用户的请求链路。
2.2 Span Attributes:细粒度的操作元数据
Span Attributes 是附加在 Span 上的键值对,用于描述该操作的具体细节。OpenTelemetry 定义了一套语义约定(Semantic Conventions),为常见场景(HTTP、gRPC、数据库、消息队列等)提供了标准化的属性键名。
| 属性键(Key) | 值类型 | 示例值 | 说明 |
|---|---|---|---|
http.method |
string | GET |
HTTP 请求方法 |
http.url |
string | https://api.example.com/users |
完整的请求 URL |
http.status_code |
int | 200 |
HTTP 响应状态码 |
db.system |
string | postgresql |
数据库类型 |
db.statement |
string | SELECT * FROM users WHERE id = ? |
数据库查询语句(建议脱敏) |
messaging.destination |
string | order.created |
消息队列主题或队列名 |
2.3 Resource:服务与环境的身份标识
Resource 代表产生可观测性信号的实体,通常是一个服务实例、一个容器、一台主机或一个 Kubernetes Pod。Resource 描述的是谁产生了数据,而不是做了什么。Resource 信息会附加到该实体产生的所有 Span、Metric 和 Log 上。
- 服务名称:
service.name是最重要的 Resource 属性,用于在 UI 中区分不同服务。 - 服务版本:
service.version用于关联代码版本与可观测性数据。 - 部署环境:
deployment.environment用于区分生产、预发、测试环境。 - 主机信息:
host.name、host.id、os.type等。 - 容器/平台信息:
container.id、k8s.pod.name、cloud.provider等。
2.4 Resource Attributes:全局的上下文元数据
Resource Attributes 是附加在 Resource 上的键值对,它们会被自动合并到该 Resource 产生的所有 Span 和 Metric 中。这意味着你不需要在每个 Span 上重复注入环境信息,只需在 Resource 层面声明一次。
// Python 示例:在 SDK 初始化时设置 Resource
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
resource = Resource.create({
"service.name": "order-service",
"service.version": "1.2.3",
"deployment.environment": "production",
"host.name": "ip-10-0-1-100.ec2.internal",
"cloud.provider": "aws",
"cloud.region": "us-east-1"
})
provider = TracerProvider(resource=resource)
2.5 Instrumentation Scope:库与模块的归属标记
Instrumentation Scope 用于标识产生 Span 或 Metric 的库、模块或组件。它解决了“这段可观测性数据是由哪个库生成的”问题。例如,一个 HTTP 请求的 Span 可能由 opentelemetry-instrumentation-flask 库创建,而一个数据库查询 Span 则由 opentelemetry-instrumentation-psycopg2 创建。
- Scope Name:通常是库的包名或模块名,例如
io.opentelemetry.flask。 - Scope Version:库的版本号,用于追踪 instrumentation 库的变更。
- 作用:在 UI 中按 instrumentation 库过滤数据,方便排查特定库版本引入的问题。
2.6 三者的关系与数据流
下图展示了 Span、Resource 和 Instrumentation Scope 在 OpenTelemetry 数据模型中的层级关系。每个 Span 都归属于一个 Instrumentation Scope,并且所有 Span 都继承自同一个 Resource 的属性。
2.7 属性注入的优先级与合并规则
当同一个属性键在多个层级出现时,OpenTelemetry 遵循明确的合并规则。理解这些规则可以避免元数据被意外覆盖。
| 层级 | 优先级 | 示例 |
|---|---|---|
| Span Attributes | 最高 | Span 上显式设置的 http.method 会覆盖 Resource 中的同名属性 |
| Instrumentation Scope Attributes | 中 | Scope 级别的属性会覆盖 Resource 属性,但被 Span 属性覆盖 |
| Resource Attributes | 最低(基础) | 作为全局默认值,被所有下级继承 |
2.8 实战:在 Python 中注入 Span 与 Resource 属性
以下代码演示了如何在 Python 中同时设置 Resource 属性和 Span 属性,并观察它们的合并效果。
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
# 1. 创建 Resource,注入全局属性
resource = Resource.create({
"service.name": "payment-service",
"service.version": "2.0.0",
"deployment.environment": "staging"
})
# 2. 配置 TracerProvider
provider = TracerProvider(resource=resource)
processor = SimpleSpanProcessor(OTLPSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# 3. 获取 Tracer
tracer = trace.get_tracer(__name__, "1.0.0")
# 4. 创建 Span 并注入 Span 属性
with tracer.start_as_current_span("process_payment") as span:
span.set_attribute("payment.amount", 99.99)
span.set_attribute("payment.currency", "USD")
span.set_attribute("payment.method", "credit_card")
# Resource 属性(service.name, version, environment)会自动附加到此 Span
print("Span 已创建,Resource 属性自动继承")
在这个例子中,payment-service、2.0.0 和 staging 三个 Resource 属性会自动出现在 process_payment Span 中,无需在每个 Span 上重复设置。而 payment.amount 等 Span 属性则描述了本次支付操作的具体细节。
2.9 概念对比总结
| 概念 | 作用域 | 生命周期 | 典型用途 | 是否可被下级覆盖 |
|---|---|---|---|---|
| Resource | 全局(所有信号) | 进程级别,启动时确定 | 服务名称、版本、主机、环境 | 是(被 Scope 和 Span 覆盖) |
| Instrumentation Scope | 库/模块级别 | 库加载时确定 | 库名称、库版本 | 是(被 Span 覆盖) |
| Span | 单次操作 | 请求级别,动态创建 | HTTP 方法、状态码、业务 ID | 最高优先级,不可被覆盖 |