2. 数据模型与语义约定:Span、Metric、Log 的数据结构,Resource 与 Attribute,语义约定规范
在 OpenTelemetry 体系中,数据模型定义了可观测性数据的标准结构,而语义约定则规定了如何命名和描述这些数据。理解这两者是进行高效数据传输与性能调优的基础。本章将深入剖析 Span、Metric、Log 三大信号的数据结构,解析 Resource 与 Attribute 的关联关系,并梳理关键的语义约定规范。
2.1 三大信号的数据结构
OpenTelemetry 将可观测性数据抽象为三种核心信号:Traces(追踪)、Metrics(指标) 和 Logs(日志)。每种信号都有其特定的数据结构,用于承载不同的信息维度。
2.1.1 Span 数据结构
Span 是 Trace 的基本构建块,代表一个工作单元。其核心数据结构包含以下字段:
| 字段 | 类型 | 描述 |
|---|---|---|
TraceId |
16 字节数组 | 全局唯一标识一条完整的 Trace 链路。 |
SpanId |
8 字节数组 | 唯一标识当前 Span。 |
ParentSpanId |
8 字节数组 | 父 Span 的 ID,用于构建 Span 之间的父子关系。 |
Name |
字符串 | Span 的描述性名称,例如 HTTP GET /api/users。 |
Kind |
枚举 | Span 的角色:CLIENT、SERVER、INTERNAL、PRODUCER、CONSUMER。 |
StartTime |
时间戳 | Span 开始的时间。 |
EndTime |
时间戳 | Span 结束的时间。 |
Status |
对象 | 执行结果:Unset、Ok、Error,并附带描述信息。 |
Attributes |
键值对列表 | 附加的元数据,如 HTTP 状态码、数据库语句等。 |
Events |
事件列表 | Span 生命周期内发生的带时间戳的注解事件。 |
Links |
链接列表 | 关联到其他 Trace 中的 Span,用于跨系统关联。 |
Resource |
对象 | 描述生成此 Span 的资源实体(如主机、容器、服务)。 |
Attributes 和 Events 是数据量的主要来源。在生产环境中,应避免添加高基数(High Cardinality)的 Attribute(如用户 ID、Session ID),这会显著增加存储和传输开销。建议将高基数数据通过 Log 或单独的事件系统传递。
2.1.2 Metric 数据结构
Metric 用于聚合和测量系统行为。OpenTelemetry 定义了三种主要的 Metric 类型,每种都有其特定的数据结构:
- Counter(计数器):只增不减的累加值,用于记录请求总数、错误次数等。
- Histogram(直方图):对观测值进行采样并分桶统计,用于计算请求延迟的 P50、P99 等。
- Gauge(仪表盘):可任意增减的瞬时值,用于记录当前内存使用量、CPU 利用率等。
所有 Metric 共享以下核心字段:
| 字段 | 类型 | 描述 |
|---|---|---|
Name |
字符串 | 指标名称,遵循语义约定,如 http.server.request.duration。 |
Description |
字符串 | 指标含义的简要说明。 |
Unit |
字符串 | 单位,如 ms、bytes、count。 |
Attributes |
键值对列表 | 描述指标的维度,如 http.method、http.status_code。 |
Data Points |
数据点列表 | 包含时间戳和具体数值(Counter 为 AsDouble,Histogram 为桶和计数,Gauge 为瞬时值)。 |
Resource |
对象 | 描述生成此 Metric 的资源实体。 |
2.1.3 Log 数据结构
Log 是带时间戳的文本记录,用于记录离散事件。OpenTelemetry 的 Log 数据模型旨在与现有日志标准(如 Syslog、JSON 格式)兼容,并统一纳入可观测性体系。
| 字段 | 类型 | 描述 |
|---|---|---|
Timestamp |
时间戳 | 日志事件发生的时间。 |
ObservedTimestamp |
时间戳 | 日志被观测系统捕获的时间。 |
SeverityText |
字符串 | 严重程度的文本表示,如 ERROR、WARN。 |
SeverityNumber |
整数 | 严重程度的数值表示,便于排序和过滤。 |
Body |
任意类型 | 日志的正文内容,可以是字符串、结构化 JSON 等。 |
Attributes |
键值对列表 | 附加的结构化上下文信息。 |
TraceId |
16 字节数组 | 关联的 Trace ID,实现 Log 与 Trace 的关联。 |
SpanId |
8 字节数组 | 关联的 Span ID,实现 Log 与 Span 的关联。 |
Resource |
对象 | 描述生成此 Log 的资源实体。 |
Body 字段如果包含大量非结构化文本,会显著增加传输和存储成本。建议优先使用结构化日志,将关键信息提取到 Attributes 中,Body 仅保留核心消息。
2.2 Resource 与 Attribute
Resource 和 Attribute 是 OpenTelemetry 数据模型中两个关键的元数据概念,它们共同为数据提供丰富的上下文。
- Resource(资源):描述生成可观测性数据的实体,通常是基础设施或服务实例。它是全局的,附加到该资源产生的所有 Span、Metric 和 Log 上。例如,一个 Kubernetes Pod 的 Resource 可能包含
service.name、k8s.pod.uid、host.name。 - Attribute(属性):附加到单个数据点(如一个 Span、一个 Metric 数据点、一条 Log)上的键值对,用于描述该数据点的具体特征。例如,一个 HTTP 请求 Span 的 Attribute 可能包含
http.method、http.url。
两者的关系是:Resource 提供全局上下文,Attribute 提供局部上下文。在数据传输和存储时,Resource 信息通常会被压缩或共享,以减少冗余。
2.3 语义约定规范
语义约定(Semantic Conventions)是 OpenTelemetry 社区定义的命名规范,旨在确保不同服务和工具生成的数据具有一致的语义,从而实现互操作性。它涵盖了命名空间、属性名称、指标名称和日志字段的标准化。
2.3.1 命名空间与属性命名
语义约定采用点分隔的命名空间,例如:
http:用于 HTTP 相关的属性,如http.method、http.status_code。db:用于数据库相关的属性,如db.system、db.statement。messaging:用于消息系统相关的属性,如messaging.system、messaging.destination。
属性名称使用小写字母和下划线,例如 http.request.method、server.address。
2.3.2 关键语义约定示例
以下是一些常用的语义约定示例:
| 信号 | 命名空间 | 属性示例 | 描述 |
|---|---|---|---|
| Trace | http |
http.request.method |
HTTP 请求方法,如 GET、POST。 |
| Trace | http |
http.response.status_code |
HTTP 响应状态码,如 200、404。 |
| Metric | http |
http.server.request.duration |
HTTP 服务端请求延迟指标。 |
| Metric | db |
db.client.connections.usage |
数据库客户端连接使用率指标。 |
| Log | log |
log.severity |
日志严重程度。 |
| Resource | service |
service.name |
服务名称。 |
| Resource | host |
host.name |
主机名。 |
2.3.3 遵循语义约定的好处
- 互操作性:不同语言、不同框架的组件可以无缝集成。
- 可分析性:后端分析工具(如 Jaeger、Prometheus)可以基于标准属性进行自动关联和可视化。
- 可维护性:团队之间可以共享通用的查询和告警规则。
mycompany.* 前缀以避免冲突)。
2.4 数据模型与语义约定关系图
下图展示了 Span、Metric、Log 三大信号如何通过 Resource 和 Attribute 进行关联,并遵循统一的语义约定规范。
2.5 数据传输与性能调优要点
理解数据模型和语义约定后,可以从以下角度进行性能调优:
- 减少 Attribute 数量:每个 Span/Metric/Log 携带的 Attribute 数量直接影响序列化和传输开销。建议将每个数据点的 Attribute 数量控制在 10 个以内。
- 避免高基数 Attribute:如
user.id、session.id等取值无限的 Attribute 会导致后端存储索引膨胀,查询性能下降。应将其通过 Log 或独立的事件系统传递。 - 利用 Resource 压缩:由于 Resource 是全局共享的,传输协议(如 OTLP)会对其进行压缩,避免重复传输。确保将公共信息放在 Resource 中。
- 遵循语义约定:使用标准属性名称可以触发后端系统的自动优化路径(如自动索引、预聚合),避免因自定义属性导致的额外处理开销。
- 采样策略:对于 Trace,结合语义约定中的
http.status_code等属性,实现基于错误或延迟的智能采样,减少不必要的数据传输。