OpenTelemetry 日志、指标与链路如何关联

用 TraceId、SpanId、Resource 和语义约定,把日志、指标与分布式链路串成可定位的排障上下文。

袋鼠云
袋鼠云
生产制造分析

OpenTelemetry 中,日志与链路的直接关联依靠同一执行上下文中的 TraceId 和 SpanId,跨服务的统一归属依靠 Resource,指标则主要通过时间窗口、资源属性和统一的语义命名与其他信号对齐。要让排障真正可用,不能只把三种数据送到同一个后端,而要在请求传播、日志注入、资源标识、指标标签和采集管道上保持同一套约定。

先分清四种上下文

TraceId 标识一次端到端请求或事务,属于同一条 Trace 的 Span 共享它;SpanId 标识其中一个具体操作,例如网关接收请求、调用数据库或发送消息。日志记录可以带这两个字段,从而直接跳到产生该日志的 Span。没有执行上下文的后台日志,不能为了“看起来统一”而伪造 TraceId。

Resource 描述产生遥测数据的实体,例如服务名、实例、容器或主机。它更像“谁发出的”,而不是“这条日志发生了什么”。同一来源的一批日志可以共享 Resource。事件发生时才变化的信息,如订单号、HTTP 路径或错误类型,应放在日志 Attributes 或 Span Attributes 中,而不是写进 Resource。

InstrumentationScope 描述发出遥测数据的仪器范围,例如某个库或组件的名称与版本。它对区分自动插桩、手工埋点和不同版本的采集器很有帮助,但不能替代 service.name。服务归属、实例归属和仪器来源应分别保留。

最后是语义约定。OpenTelemetry 的语义约定为常见操作和数据定义一致的键和值,例如服务、异常、HTTP、数据库和消息操作。它解决的是“同一个概念叫什么”,不自动解决“有没有生成这条数据”。字段名统一但没有传播上下文,仍然无法关联。

第一步:统一 Resource 身份

每个服务先确定稳定的 service.name,再补充部署环境、实例标识和版本等资源属性。服务名不能随着 Pod 名称、发布批次或进程重启变化,否则同一服务会被拆成大量来源。实例标识可以变化,但服务级聚合必须依赖稳定的服务名。

在请求入口、异步消费者和定时任务上分别定义资源边界。HTTP 服务应把入口服务作为当前 Trace 的来源;消息消费者要保留消息传播过来的上下文,若一个批次合并了多个来源,则可以把来源作为链接或批次属性,而不是随便选择一个父 Span。定时任务没有上游请求时,可以为一次执行创建新的根 Trace,并把任务名称和执行批次放进属性。

第二步:让上下文跨进程传递

Trace Context 需要在服务边界传播。HTTP 请求、RPC 和消息头都应使用约定的传播格式;调用方创建子 Span,被调用方从入站上下文继续 Trace。最容易遗漏的是异步队列:生产者有 Trace,消费者却重新创建一条没有关联的新 Trace,结果业务链路在消息边界断开。

批量处理需要谨慎。一个消费者可能从多个消息中取出事件,再用一个 Span 处理整个批次,此时并不存在一个天然的单一父请求。可以使用 Span Links 表达因果关联,或者让日志保留批次标识和消息标识。不要把第一个消息的 TraceId 复制给整批所有事件,这会制造错误的链路关系。

智能运维 YCOS

统一指标、日志、链路,覆盖监控、告警、根因定位与智能运维的可观测平台

了解产品

第三步:把 TraceId 和 SpanId 写进日志

OpenTelemetry Logs 数据模型中的 LogRecord 可以包含时间戳、严重级别、Body、Attributes、Resource、InstrumentationScope、TraceId 和 SpanId。应用仍可使用原有日志库,再通过日志桥接或 SDK 把当前上下文注入记录;也可以直接使用 Logs API 发出结构化日志。关键不是日志是否仍写入文件,而是采集时不能丢掉上下文字段。

建议日志至少保留如下信息:

{
  "timestamp": "2026-07-29T10:31:02.123Z",
  "severity_text": "ERROR",
  "body": "database timeout",
  "trace_id": "32位十六进制TraceId",
  "span_id": "16位十六进制SpanId",
  "service.name": "order-api",
  "error.type": "TimeoutError",
  "db.system": "postgresql"
}

如果 SpanId 存在,按日志数据模型应同时保留 TraceId;没有 Trace 上下文的启动日志可以只保留 Resource 和时间。异常信息宜遵循异常语义约定,避免一半服务写 exception,另一半服务写 err,导致查询需要维护两套规则。

第四步:让指标和链路共享定位维度

指标通常以时间序列聚合,适合回答“哪个服务、哪类操作、在什么时间段变慢或出错”。日志和 Trace 适合回答“某一次请求具体经过了什么”。因此不要把每个用户 ID、订单号或 TraceId 都做成指标标签,这会造成高基数和存储压力。指标标签应保留低基数的服务、路由模板、状态类别和依赖类型,单次请求的 TraceId 放在日志或链路中。

在支持 exemplars 的指标链路中,可以把某个指标样本与 Trace 关联,让排障人员从延迟或错误率曲线跳到代表性 Trace;没有该能力时,仍可用同一时间窗口、服务名、实例和操作名缩小范围,再从 Trace 的 Span 属性和日志上下文继续定位。不要把“指标能跳链路”当作三类信号自动具备的能力,它取决于 SDK、Collector、存储后端和查询界面是否共同保留关联信息。

能源矿产

覆盖矿山、冶炼、电力与集团统管场景,让生产现场看得清、安全风险防得早、能效成本算得明、集团经营管得透。

了解解决方案

第五步:检查 Collector 是否破坏关联

Collector 的日志接收器、处理器和导出器应被视为数据契约的一部分。文件日志需要正确解析时间、严重级别和结构化字段;解析器不能把 JSON 作为一整段 Body 后丢掉 trace_id。处理器可以补充资源属性或统一命名,但不应无意删除 TraceId、SpanId、Resource 或事件时间。不同信号发往不同后端时,要检查三边的服务名、环境属性和时间精度是否一致。

采样也是关联设计的一部分。若 Trace 被采样丢弃,而日志仍然保留 TraceId,日志可以指向一条不存在的 Trace;若为节省成本删除日志上下文,Trace 又无法解释具体错误。应明确哪些错误、慢请求和关键业务操作保留更高采样率,并在保留策略中同时考虑日志与链路,而不是只调整其中一种信号。

在企业统一运维场景中,袋鼠云 YCOS 的官方资料将 OpenTelemetry 兼容以及指标、日志、链路的统一分析列为能力范围;落地时仍应按实际版本核对采集器、字段映射和后端查询配置。

用一个故障例子验证闭环

假设接口延迟曲线在 10:30 升高。先用 service.name、路由和时间窗口筛选指标,确认是否只有一个实例或一个依赖受影响;再从代表性 Trace 查看各 Span 的耗时,判断是数据库、RPC 还是队列等待;最后在同一 SpanId 下检索日志,读取超时参数、异常类型和请求上下文。若只能看到一条没有 TraceId 的错误日志,优先修复日志注入,而不是继续调整查询语句。

验证时可以故意制造四种情况:跨 HTTP 调用上下文不断、跨消息调用上下文不断、没有 Trace 的启动日志可按 Resource 查询、批量消费者不会把多个来源错误合成一个父 Span。每种情况都应能解释“为什么能关联”或“为什么只能按时间和来源关联”。

常见失败模式

第一,服务名使用实例名,发布后历史趋势被切断。第二,只在网关生成 TraceId,下游没有传播。第三,日志采集把字段放进 Body,查询后端却按顶层属性检索。第四,把高基数业务字段塞进指标标签,短期方便筛选,长期造成成本和查询退化。第五,异步批处理强行使用单一父 Span,形成看似完整、实际错误的链路。第六,语义约定版本变化后只改生产端,查询和告警仍使用旧字段。

建议先选一条关键请求链路完成端到端验证,再推广到异步、定时和批处理任务。验收不看信号数量,而看能否从一个指标异常进入一条 Trace,再从一个 Span 找到对应日志,并且服务、实例、环境和时间语义在三个信号中保持一致。

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信