4. OpenTelemetry 日志数据模型:LogRecord 结构、属性、资源与作用域
在 OpenTelemetry(OTel)体系中,日志并非简单的文本字符串,而是被抽象为一种结构化的数据模型。理解 LogRecord 的构成,是标准化日志采集的基石。本章将深入剖析 LogRecord 的核心组件:结构体字段、属性(Attributes)、资源(Resource) 和 作用域(Scope),并展示它们如何协同工作,形成可观测性数据的统一语义。
4.1 LogRecord 核心结构
LogRecord 是 OTel 日志数据模型中的最小独立单元,代表一条日志事件。其结构设计兼顾了 高性能 与 可扩展性,通过预定义字段与动态属性的组合,覆盖了从传统 Syslog 到现代云原生应用的所有日志场景。
下表列出了 LogRecord 的核心字段及其语义说明:
| 字段名称 | 类型 | 说明 | 示例值 |
|---|---|---|---|
Timestamp |
UnixNano | 日志事件发生的时间戳(纳秒精度) | 1697200000000000000 |
ObservedTimestamp |
UnixNano | 日志被采集系统观察到的时间戳 | 1697200001000000000 |
SeverityText |
string | 可读的严重级别文本(如 ERROR) |
ERROR |
SeverityNumber |
int | 标准化的严重级别数值(如 17 代表 Error) |
17 |
Body |
Any | 日志正文,可以是字符串或结构化值 | {"message": "Connection refused", "port": 8080} |
Attributes |
Map | 与日志事件直接相关的键值对(动态元数据) | {"http.method": "GET", "http.status_code": 500} |
TraceId |
bytes | 关联的分布式追踪 ID(用于链路关联) | 0af7651916cd43dd8448eb211c80319c |
SpanId |
bytes | 关联的 Span ID | b7ad6b7169203331 |
Resource |
Resource | 标识日志来源的实体信息(如主机、容器、服务) | {"service.name": "payment-api", "host.name": "node-1"} |
Scope |
InstrumentationScope | 标识产生日志的 instrumentation 库信息 | {"name": "my-logger", "version": "1.0.0"} |
Timestamp、SeverityNumber 和 Body。如果应用已接入分布式追踪,务必注入 TraceId 和 SpanId,实现日志与追踪的无缝关联。
4.2 属性(Attributes):动态元数据的载体
Attributes 是 LogRecord 中最灵活的部分,用于携带与日志事件相关的 上下文信息。它们遵循 OTel 的 语义约定(Semantic Conventions),确保不同服务产生的同类日志具有一致的字段命名。
- 通用属性:如
http.method、http.url、db.system、exception.type。 - 自定义属性:允许用户添加业务相关的键值对,如
order.id、user.role。 - 类型支持:属性值支持字符串、布尔、整数、浮点数、数组以及嵌套的 Map 结构。
4.3 资源(Resource):日志来源的实体标识
Resource 描述了产生日志的 实体,通常在整个服务生命周期内保持不变。它独立于 LogRecord 的 Attributes,用于在跨服务、跨主机场景下快速定位日志来源。
常见的 Resource 属性包括:
| 属性键 | 说明 | 示例值 |
|---|---|---|
service.name |
服务名称(必填) | user-service |
service.namespace |
服务所属命名空间 | production |
host.name |
主机名 | ip-10-0-1-23 |
k8s.pod.name |
Kubernetes Pod 名称 | payment-api-7d8f9c6b4c-x2y3z |
cloud.region |
云服务区域 | us-east-1 |
4.4 作用域(Scope):Instrumentation 库的标识
Scope 用于标识是 哪个 instrumentation 库 产生了这条日志。它解决了多库共存时的归属问题,使得在分析日志时可以按库进行过滤或隔离。
Scope 包含两个核心字段:
name:库的名称,如io.opentelemetry.contrib.mongodb。version:库的版本号,如1.2.3。
4.5 数据模型关系总览
下图展示了 LogRecord、Attributes、Resource、Scope 之间的层级关系:
4.6 完整 LogRecord 示例(Protobuf 表示)
以下是一个完整的 LogRecord 在 OTel 协议中的 Protobuf 序列化示例,展示了各字段如何组合在一起:
{
"resource": {
"attributes": [
{"key": "service.name", "value": {"stringValue": "order-service"}},
{"key": "host.name", "value": {"stringValue": "ip-10-0-1-45"}}
]
},
"scope": {
"name": "com.example.logger",
"version": "2.1.0"
},
"logRecords": [
{
"timeUnixNano": "1697200000000000000",
"severityNumber": 17,
"severityText": "ERROR",
"body": {"stringValue": "Failed to process payment for order 12345"},
"attributes": [
{"key": "order.id", "value": {"stringValue": "12345"}},
{"key": "payment.amount", "value": {"doubleValue": 99.99}},
{"key": "http.status_code", "value": {"intValue": 500}}
],
"traceId": "0af7651916cd43dd8448eb211c80319c",
"spanId": "b7ad6b7169203331"
}
]
}
4.7 标准化采集的落地要点
在实现日志采集标准化时,请遵循以下原则:
- 统一 Resource 注入:通过 OTel Collector 的
resourceprocessor 或 SDK 的 Resource Detector,确保所有日志都携带service.name、host.name等关键标识。 - 语义化 Attributes:优先使用 OTel 语义约定中的属性键,避免自定义命名导致的查询混乱。
- Scope 隔离:为不同的日志库(如 Logback、Log4j、自定义 Logger)设置独立的 Scope,便于在 UI 中按库过滤。
- 链路关联:在微服务架构中,务必通过
TraceId和SpanId将日志与追踪关联,实现端到端的可观测性。