4. 资源元数据基础:Resource 的创建方式与合并策略

在 OpenTelemetry 中,Resource(资源) 是描述生成遥测数据的实体(如服务、主机、容器、进程)的元数据集合。每个 Span、Metric 或 LogRecord 都可以关联一个 Resource,从而在分析时区分数据来源。本章深入讲解 Resource 的两种创建方式(手动与自动检测)以及核心的资源合并策略(Merge)。

4.1 Resource 的核心结构

Resource 本质上是一个键值对(Key-Value)集合,遵循 semantic_conventions 规范。每个 Resource 包含一个 SchemaURL 和一组属性。以下是一个典型的 Resource 示例:

Resource{
  SchemaURL: "https://opentelemetry.io/schemas/1.24.0",
  Attributes: {
    "service.name":          "payment-service",
    "service.version":       "2.1.0",
    "service.instance.id":   "inst-abc123",
    "host.name":             "node-01",
    "host.type":             "virtual_machine",
    "telemetry.sdk.name":    "opentelemetry",
    "telemetry.sdk.language":"python",
    "telemetry.sdk.version": "1.24.0"
  }
}

4.2 手动创建 Resource

手动创建 Resource 是最直接的方式,适用于已知固定元数据的场景,例如在配置文件中定义服务名称和环境标签。各语言 SDK 均提供 Resource.create() 或等效构造器。

4.2.1 各语言示例

语言 代码示例 说明
Python
from opentelemetry.sdk.resources import Resource

resource = Resource.create({
    "service.name": "order-service",
    "service.version": "1.0.0",
    "deployment.environment": "production"
})
使用字典传入属性
Go
import "go.opentelemetry.io/otel/sdk/resource"

res := resource.NewWithAttributes(
    semconv.SchemaURL,
    semconv.ServiceNameKey.String("order-service"),
    semconv.ServiceVersionKey.String("1.0.0"),
    attribute.String("deployment.environment", "production"),
)
使用 attribute 构造器
Java
import io.opentelemetry.sdk.resources.Resource;

Resource resource = Resource.create(
    Attributes.builder()
        .put("service.name", "order-service")
        .put("service.version", "1.0.0")
        .put("deployment.environment", "production")
        .build()
);
使用 Attributes.builder
最佳实践: 手动创建 Resource 时,始终优先使用语义约定(semconv)中定义的常量键名,避免拼写不一致导致的数据碎片化。

4.3 自动检测 Resource

自动检测(Auto-Detection)允许 SDK 从运行环境中自动收集 Resource 属性,例如从环境变量、云厂商元数据、容器信息、主机信息等。OpenTelemetry 提供了丰富的 Resource Detector 实现。

4.3.1 常用内置 Detector

Detector 名称 收集的属性示例 适用场景
环境变量 Detector OTEL_RESOURCE_ATTRIBUTES 中定义的所有键值 通用配置注入
主机 Detector host.name, host.id, host.arch 物理机或虚拟机
容器 Detector container.id Docker / containerd
Kubernetes Detector k8s.pod.uid, k8s.node.name, k8s.namespace.name K8s 环境
云厂商 Detector cloud.provider, cloud.region, cloud.account.id AWS / GCP / Azure
进程 Detector process.pid, process.executable.name 进程级监控

4.3.2 自动检测代码示例

# Python 示例:组合多个 Detector
from opentelemetry.sdk.resources import get_aggregated_resources
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.resources import OTELResourceDetector
from opentelemetry.sdk.resources import ProcessResourceDetector

# 自动检测并合并多个来源
resource = get_aggregated_resources(
    [
        OTELResourceDetector(),      # 从 OTEL_RESOURCE_ATTRIBUTES 环境变量
        ProcessResourceDetector(),   # 从当前进程
    ]
)
# 再与手动 Resource 合并
final_resource = resource.merge(
    Resource.create({"service.name": "my-service"})
)
注意: 自动检测依赖于运行环境的具体实现。在本地开发环境或非标准部署中,某些 Detector 可能返回空属性。建议始终保留手动 Resource 作为兜底。

4.4 资源合并策略(Merge)

当存在多个 Resource 来源时(例如手动创建 + 自动检测),需要使用 Merge 策略将它们合并为一个统一的 Resource。OpenTelemetry 的合并规则遵循“先来后到,后来覆盖”的原则,但有一个关键例外:空值不覆盖非空值。

4.4.1 Merge 规则详解

场景 旧 Resource 属性值 新 Resource 属性值 合并结果
新属性不存在 service.name: "old" 无此键 service.name: "old"(保留旧值)
新属性存在且非空 service.name: "old" service.name: "new" service.name: "new"(新值覆盖)
新属性存在但为空 service.name: "old" service.name: "" service.name: "old"(空值不覆盖)
新属性存在但为 nil service.name: "old" service.name: nil service.name: "old"(nil 不覆盖)

4.4.2 Merge 调用方式

# Python 示例
base_resource = Resource.create({"service.name": "base", "env": "dev"})
override_resource = Resource.create({"service.name": "prod", "region": "us-east"})

merged = base_resource.merge(override_resource)
# 结果: {"service.name": "prod", "env": "dev", "region": "us-east"}
# service.name 被覆盖,env 保留,region 新增
关键理解: Merge 操作是 非对称 的。调用 resource_a.merge(resource_b) 时,resource_b 中的非空属性会覆盖 resource_a 中的同名属性。通常将自动检测的 Resource 作为 resource_a,手动定义的 Resource 作为 resource_b,以确保手动配置的优先级更高。

4.5 Resource 创建与合并流程总览

下图展示了从多个来源收集 Resource 属性,并通过 Merge 策略生成最终 Resource 的完整流程:

Resource 创建与合并流程 手动 Resource Resource.create({...}) 自动检测 Resource Detector 集合 环境变量 主机信息 容器元数据 云厂商 Merge 合并 后来覆盖 + 空值保护 最终 Resource 统一属性集合 说明: 手动 Resource 优先级高于自动检测;空字符串或 nil 不会覆盖已有非空值。 最终 Resource 将关联到所有 Span、Metric 和 LogRecord。

4.6 实际应用中的合并策略选择

在真实项目中,通常采用以下分层合并策略:

  1. 第一层: 使用 OTEL_RESOURCE_ATTRIBUTES 环境变量注入全局默认属性(如环境、区域)。
  2. 第二层: 自动检测 Detector 收集主机、容器、云平台等运行时信息。
  3. 第三层: 手动创建 Resource 覆盖服务名称、版本等业务关键属性。
  4. 最终合并: 按 auto_detected.merge(manual) 顺序合并,确保手动配置的优先级最高。
推荐模式: 在 SDK 初始化时,先调用 get_aggregated_resources() 收集所有自动检测属性,再与手动 Resource 合并,最后将结果设置到 TracerProvider 或 MeterProvider 中。这样既保留了环境自动发现的灵活性,又保证了关键业务属性的可控性。