OpenTelemetry 核心概念

理解 OpenTelemetry 的核心概念对于正确使用和配置至关重要。本文将介绍分布式追踪、指标和日志的基本概念。

目录


Trace(追踪)

什么是 Trace?

Trace 表示一个请求在分布式系统中的完整执行路径。一个 Trace 由多个 Span 组成,这些 Span 按照调用关系形成树状结构。

Trace 示例

假设有一个电商系统的下单请求:

Trace: Place Order (总耗时: 245ms)
├─ Span: API Gateway (35ms)
│ ├─ Span: Auth Service (15ms)
│ └─ Span: Order Service (210ms)
│ ├─ Span: Inventory Service (80ms)
│ │ └─ Span: Database Query (25ms)
│ ├─ Span: Payment Service (95ms)
│ │ ├─ Span: HTTP Call to Payment Gateway (70ms)
│ │ └─ Span: Database Update (15ms)
│ └─ Span: Notification Service (30ms)
│ └─ Span: Send Email (25ms)

Trace 的组成

  • Trace ID: 唯一标识一个 Trace,32 位十六进制字符串
  • Span 列表: 组成这个 Trace 的所有 Span
  • 时间范围: 从第一个 Span 开始到最后一个 Span 结束

Trace 的价值

  1. 可视化调用链路: 清晰展示服务间的调用关系
  2. 性能分析: 识别慢查询和性能瓶颈
  3. 故障定位: 快速找到错误发生的位置
  4. 依赖分析: 了解服务间的依赖关系

Span(跨度)

什么是 Span?

Span 是 Trace 的基本构建块,表示一个操作或工作单元。每个 Span 包含:

  • 名称: 操作的描述性名称
  • 开始时间: 操作开始的时间戳
  • 结束时间: 操作结束的时间戳
  • 属性: 键值对形式的元数据
  • 事件: 操作过程中发生的事件
  • 状态: 操作的执行状态(成功、失败等)
  • 父 Span ID: 调用者的 Span ID(如果有)

Span 的类型

根据调用关系,Span 可以分为:

  1. Root Span: 没有父 Span 的顶层 Span
  2. Child Span: 有父 Span 的 Span
  3. Internal Span: 内部方法调用
  4. Client Span: 客户端发起的请求
  5. Server Span: 服务端接收的请求
  6. Producer Span: 消息生产者
  7. Consumer Span: 消息消费者

Span 的结构示例

{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"parent_span_id": "00f067aa0ba90000",
"name": "GET /api/users/{id}",
"kind": "SERVER",
"start_time": "2024-01-15T10:30:00.123456Z",
"end_time": "2024-01-15T10:30:00.145678Z",
"duration_ms": 22.222,
"status": {
"code": "OK"
},
"attributes": {
"http.method": "GET",
"http.url": "/api/users/123",
"http.status_code": 200,
"http.user_agent": "Mozilla/5.0...",
"service.name": "user-service",
"service.version": "1.2.3"
},
"events": [
{
"name": "cache_miss",
"timestamp": "2024-01-15T10:30:00.125000Z",
"attributes": {
"cache.key": "user:123"
}
}
]
}

Span 的属性(Attributes)

属性是描述 Span 的键值对,遵循语义约定:

HTTP 请求:

  • http.method: GET, POST, PUT, DELETE
  • http.url: 完整 URL
  • http.status_code: HTTP 状态码
  • http.route: 路由模板(如 /api/users/{id})

数据库操作:

  • db.system: mysql, postgresql, mongodb
  • db.name: 数据库名称
  • db.statement: SQL 语句
  • db.operation: SELECT, INSERT, UPDATE

消息队列:

  • messaging.system: kafka, rabbitmq
  • messaging.destination: 主题或队列名称
  • messaging.operation: publish, receive

Span 的事件(Events)

事件表示 Span 执行过程中的时间点:

span.AddEvent("cache_miss", trace.WithAttributes(
attribute.String("cache.key", "user:123"),
))
span.AddEvent("retry_attempt", trace.WithAttributes(
attribute.Int("retry.count", 2),
))
span.AddEvent("exception", trace.WithAttributes(
attribute.String("exception.type", "TimeoutError"),
attribute.String("exception.message", "Connection timeout"),
))

Context(上下文)

什么是 Context?

Context 是 OpenTelemetry 中传递跨度信息的机制,确保分布式系统中的 Span 能够正确关联。

Context Propagation(上下文传播)

在分布式系统中,请求会跨越多个服务。Context Propagation 确保 Trace 信息能够在服务间传递。

传播方式:

  1. HTTP Headers:

    traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
    tracestate: vendor1=value1,vendor2=value2
  2. gRPC Metadata:

    grpc-trace-bin: <binary trace context>
  3. 消息队列 Headers:

    traceparent: <trace context>

W3C Trace Context

OpenTelemetry 使用 W3C Trace Context 标准:

  • traceparent: 必需,包含 Trace ID 和 Span ID
  • tracestate: 可选,供应商特定的追踪信息

traceparent 格式:

version-trace_id-parent_id-trace_flags
00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
│ │ │ │
│ │ │ └─ flags (01=sampled)
│ │ └──────────────────── parent span id (16 hex)
│ └────────────────────────────────────────────────────── trace id (32 hex)
└───────────────────────────────────────────────────────── version

Context 的使用

Go 示例:

// 提取上下文
ctx := otel.GetTextMapPropagator().Extract(
context.Background(),
propagation.HeaderCarrier(r.Header),
)
// 使用上下文创建 Span
ctx, span := tracer.Start(ctx, "operation_name")
defer span.End()
// 注入上下文到下游请求
otel.GetTextMapPropagator().Inject(
ctx,
propagation.HeaderCarrier(req.Header),
)

Resource(资源)

什么是 Resource?

Resource 描述生成遥测数据的实体,如服务、主机、容器等。Resource 属性在整个进程生命周期内保持不变。

常见的 Resource 属性

服务信息:

  • service.name: 服务名称(必需)
  • service.version: 服务版本
  • service.namespace: 服务命名空间
  • service.instance.id: 服务实例 ID

主机信息:

  • host.name: 主机名
  • host.type: 主机类型(physical, virtual, cloud)
  • host.arch: CPU 架构(amd64, arm64)
  • host.id: 主机唯一标识

容器信息:

  • container.name: 容器名称
  • container.id: 容器 ID
  • container.image.name: 镜像名称
  • container.image.tag: 镜像标签

Kubernetes 信息:

  • k8s.namespace.name: Kubernetes 命名空间
  • k8s.pod.name: Pod 名称
  • k8s.deployment.name: Deployment 名称
  • k8s.node.name: 节点名称

云平台信息:

  • cloud.provider: aws, azure, gcp
  • cloud.region: us-east-1, us-west-2
  • cloud.availability_zone: us-east-1a

配置 Resource

环境变量:

export OTEL_SERVICE_NAME=user-service
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3,deployment.environment=production

代码配置:

resource, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("user-service"),
semconv.ServiceVersion("1.2.3"),
attribute.String("deployment.environment", "production"),
),
)

Instrumentation(埋点)

什么是 Instrumentation?

Instrumentation 是向应用程序添加可观测性代码的过程。OpenTelemetry 支持两种埋点方式:

1. 自动埋点(Auto-Instrumentation)

无需修改代码,通过 Agent 或库自动注入埋点代码。

优点:

  • 快速接入
  • 无需修改代码
  • 自动更新

支持的组件:

  • Web 框架
  • HTTP 客户端
  • 数据库驱动
  • 消息队列客户端
  • RPC 框架

示例(Python):

opentelemetry-instrument python app.py

2. 手动埋点(Manual Instrumentation)

在代码中显式调用 OpenTelemetry API。

优点:

  • 精确控制
  • 自定义属性
  • 业务埋点

示例(Go):

func ProcessOrder(ctx context.Context, orderID string) error {
ctx, span := tracer.Start(ctx, "process_order",
trace.WithAttributes(
attribute.String("order.id", orderID),
),
)
defer span.End()
// 业务逻辑
if err := validateOrder(ctx, orderID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}
span.AddEvent("order_validated")
return nil
}

Instrumentation Library

Instrumentation Library 是提供自动埋点的库:

  • 名称: 库的标识符(如 io.opentelemetry.instrumentation.gin)
  • 版本: 库的版本号
  • Schema URL: 语义约定的版本

Exporter(导出器)

什么是 Exporter?

Exporter 负责将遥测数据发送到后端系统。OpenTelemetry 支持多种导出协议。

常见的 Exporter

  1. OTLP(推荐):

    • OpenTelemetry Protocol
    • 官方标准协议
    • 支持 gRPC 和 HTTP
    • 支持所有信号类型
  2. Jaeger:

    • Jaeger 原生格式
    • 仅支持 Traces
  3. Zipkin:

    • Zipkin 格式
    • 仅支持 Traces
  4. Prometheus:

    • Prometheus 格式
    • 仅支持 Metrics
  5. Console(调试):

    • 输出到控制台
    • 用于开发和调试

OTLP Exporter 配置

环境变量:

# Endpoint
export OTEL_EXPORTER_OTLP_ENDPOINT=https://your-apm-platform.com
# Protocol (grpc or http/protobuf)
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
# Headers (如认证信息)
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=<your-dsn>
# 超时时间(毫秒)
export OTEL_EXPORTER_OTLP_TIMEOUT=10000

分信号配置:

# 仅 Traces
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://traces.example.com
export OTEL_EXPORTER_OTLP_TRACES_HEADERS=api-key=xxx
# 仅 Metrics
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metrics.example.com
# 仅 Logs
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://logs.example.com

Processor(处理器)

什么是 Processor?

Processor 在 Span 导出前对其进行处理。常见的处理器包括:

1. Batch Processor(批量处理器)

将 Span 批量导出,提高性能:

bsp := sdktrace.NewBatchSpanProcessor(
exporter,
sdktrace.WithMaxExportBatchSize(512),
sdktrace.WithBatchTimeout(5 * time.Second),
sdktrace.WithMaxQueueSize(2048),
)

配置参数:

  • MaxExportBatchSize: 批量大小(默认 512)
  • BatchTimeout: 批量超时时间(默认 5s)
  • MaxQueueSize: 队列大小(默认 2048)

2. Simple Processor(简单处理器)

立即同步导出,适用于测试:

sp := sdktrace.NewSimpleSpanProcessor(exporter)

3. Filtering Processor(过滤处理器)

根据条件过滤 Span(需自定义实现)。


Sampler(采样器)

什么是 Sampler?

Sampler 决定哪些 Span 应该被记录和导出。合理的采样可以降低成本和性能开销。

采样策略

  1. AlwaysOn:

    • 采样所有 Span
    • 适用于低流量或开发环境
  2. AlwaysOff:

    • 不采样任何 Span
    • 适用于禁用追踪
  3. TraceIDRatioBased:

    • 基于 Trace ID 的概率采样
    • 采样率:0.0 ~ 1.0
    • 确保同一 Trace 的所有 Span 采样决策一致
  4. ParentBased:

    • 根据父 Span 的采样决策
    • 默认策略,保证 Trace 完整性

配置采样器

环境变量:

# 采样器类型
export OTEL_TRACES_SAMPLER=traceidratio
# 采样率 (10%)
export OTEL_TRACES_SAMPLER_ARG=0.1
# 父级采样 + 10% 采样率
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1

代码配置:

tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(
sdktrace.ParentBased(
sdktrace.TraceIDRatioBased(0.1),
),
),
)

采样决策流程

请求到达
│
▼
是否有父 Span?
│
├─ 是 → 使用父 Span 的采样决策
│
└─ 否 → 根据采样率决策
│
├─ 采样 → 记录并导出
│
└─ 不采样 → 不记录

Metrics(指标)

什么是 Metrics?

Metrics 是系统状态的数字测量值,包括:

  • Counter(计数器): 单调递增的值(如请求总数)
  • UpDownCounter(升降计数器): 可增可减的值(如活跃连接数)
  • Histogram(直方图): 值的分布(如请求延迟)
  • Gauge(仪表盘): 瞬时值(如 CPU 使用率)

Metric 的组成

  • 名称: 指标名称(如 http.server.duration)
  • 描述: 指标说明
  • 单位: 测量单位(如 ms, bytes, 1)
  • 类型: Counter, Histogram 等
  • 属性: 维度信息(如 http.method, http.status_code)

使用 Metrics

Counter 示例:

// 创建 Counter
counter, _ := meter.Int64Counter(
"http.server.requests",
metric.WithDescription("Total HTTP requests"),
metric.WithUnit("1"),
)
// 增加计数
counter.Add(ctx, 1,
metric.WithAttributes(
attribute.String("http.method", "GET"),
attribute.Int("http.status_code", 200),
),
)

Histogram 示例:

// 创建 Histogram
histogram, _ := meter.Float64Histogram(
"http.server.duration",
metric.WithDescription("HTTP request duration"),
metric.WithUnit("ms"),
)
// 记录值
histogram.Record(ctx, duration.Milliseconds(),
metric.WithAttributes(
attribute.String("http.method", "GET"),
attribute.String("http.route", "/api/users"),
),
)

Logs(日志)

什么是 Logs?

Logs 记录应用程序的事件和状态变化。OpenTelemetry 提供结构化日志支持。

Log 的特点

  • 与 Trace 关联: 自动携带 Trace ID 和 Span ID
  • 结构化: 键值对形式,易于查询
  • 统一格式: 跨语言的一致性

Log 与 Trace 的关联

{
"timestamp": "2024-01-15T10:30:00.123Z",
"severity": "INFO",
"body": "User login successful",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"attributes": {
"user.id": "12345",
"user.name": "john.doe"
}
}

使用 Logs

集成日志库(推荐):

大多数语言都支持将现有日志库与 OpenTelemetry 集成:

  • Go: zap, logrus, slog
  • Python: logging, structlog
  • Java: Log4j, Logback, SLF4J
  • JavaScript: winston, pino
  • .NET: Serilog, NLog, Microsoft.Extensions.Logging

示例(Python + logging):

import logging
from opentelemetry.instrumentation.logging import LoggingInstrumentor
LoggingInstrumentor().instrument()
logger = logging.getLogger(__name__)
logger.info("User logged in", extra={"user_id": "12345"})

自动添加 trace_id 和 span_id 到日志中。


总结

理解这些核心概念是使用 OpenTelemetry 的基础:

  1. Trace 和 Span: 分布式追踪的核心,记录调用链路
  2. Context: 确保 Span 在分布式系统中正确关联
  3. Resource: 描述生成遥测数据的实体
  4. Instrumentation: 添加可观测性代码的方式
  5. Exporter: 将数据发送到后端
  6. Processor: 处理 Span 的中间层
  7. Sampler: 控制采样率,平衡成本和观测性
  8. Metrics: 系统状态的数字测量
  9. Logs: 结构化日志,与 Trace 关联

下一步

参考资源

在Gitlab上编辑本文档