OpenTelemetry 核心概念
理解 OpenTelemetry 的核心概念对于正确使用和配置至关重要。本文将介绍分布式追踪、指标和日志的基本概念。
目录
- Trace(追踪)
- Span(跨度)
- Context(上下文)
- Resource(资源)
- Instrumentation(埋点)
- Exporter(导出器)
- Processor(处理器)
- Sampler(采样器)
- Metrics(指标)
- Logs(日志)
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 的价值
- 可视化调用链路: 清晰展示服务间的调用关系
- 性能分析: 识别慢查询和性能瓶颈
- 故障定位: 快速找到错误发生的位置
- 依赖分析: 了解服务间的依赖关系
Span(跨度)
什么是 Span?
Span 是 Trace 的基本构建块,表示一个操作或工作单元。每个 Span 包含:
- 名称: 操作的描述性名称
- 开始时间: 操作开始的时间戳
- 结束时间: 操作结束的时间戳
- 属性: 键值对形式的元数据
- 事件: 操作过程中发生的事件
- 状态: 操作的执行状态(成功、失败等)
- 父 Span ID: 调用者的 Span ID(如果有)
Span 的类型
根据调用关系,Span 可以分为:
- Root Span: 没有父 Span 的顶层 Span
- Child Span: 有父 Span 的 Span
- Internal Span: 内部方法调用
- Client Span: 客户端发起的请求
- Server Span: 服务端接收的请求
- Producer Span: 消息生产者
- 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, DELETEhttp.url: 完整 URLhttp.status_code: HTTP 状态码http.route: 路由模板(如/api/users/{id})
数据库操作:
db.system: mysql, postgresql, mongodbdb.name: 数据库名称db.statement: SQL 语句db.operation: SELECT, INSERT, UPDATE
消息队列:
messaging.system: kafka, rabbitmqmessaging.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 信息能够在服务间传递。
传播方式:
HTTP Headers:
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01tracestate: vendor1=value1,vendor2=value2gRPC Metadata:
grpc-trace-bin: <binary trace context>消息队列 Headers:
traceparent: <trace context>
W3C Trace Context
OpenTelemetry 使用 W3C Trace Context 标准:
- traceparent: 必需,包含 Trace ID 和 Span ID
- tracestate: 可选,供应商特定的追踪信息
traceparent 格式:
version-trace_id-parent_id-trace_flags00-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),)// 使用上下文创建 Spanctx, 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: 容器 IDcontainer.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, gcpcloud.region: us-east-1, us-west-2cloud.availability_zone: us-east-1a
配置 Resource
环境变量:
export OTEL_SERVICE_NAME=user-serviceexport 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
OTLP(推荐):
- OpenTelemetry Protocol
- 官方标准协议
- 支持 gRPC 和 HTTP
- 支持所有信号类型
Jaeger:
- Jaeger 原生格式
- 仅支持 Traces
Zipkin:
- Zipkin 格式
- 仅支持 Traces
Prometheus:
- Prometheus 格式
- 仅支持 Metrics
Console(调试):
- 输出到控制台
- 用于开发和调试
OTLP Exporter 配置
环境变量:
# Endpointexport 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
分信号配置:
# 仅 Tracesexport OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://traces.example.comexport OTEL_EXPORTER_OTLP_TRACES_HEADERS=api-key=xxx# 仅 Metricsexport OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metrics.example.com# 仅 Logsexport 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 应该被记录和导出。合理的采样可以降低成本和性能开销。
采样策略
AlwaysOn:
- 采样所有 Span
- 适用于低流量或开发环境
AlwaysOff:
- 不采样任何 Span
- 适用于禁用追踪
TraceIDRatioBased:
- 基于 Trace ID 的概率采样
- 采样率:0.0 ~ 1.0
- 确保同一 Trace 的所有 Span 采样决策一致
ParentBased:
- 根据父 Span 的采样决策
- 默认策略,保证 Trace 完整性
配置采样器
环境变量:
# 采样器类型export OTEL_TRACES_SAMPLER=traceidratio# 采样率 (10%)export OTEL_TRACES_SAMPLER_ARG=0.1# 父级采样 + 10% 采样率export OTEL_TRACES_SAMPLER=parentbased_traceidratioexport 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 示例:
// 创建 Countercounter, _ := 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 示例:
// 创建 Histogramhistogram, _ := 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 loggingfrom opentelemetry.instrumentation.logging import LoggingInstrumentorLoggingInstrumentor().instrument()logger = logging.getLogger(__name__)logger.info("User logged in", extra={"user_id": "12345"})
自动添加 trace_id 和 span_id 到日志中。
总结
理解这些核心概念是使用 OpenTelemetry 的基础:
- Trace 和 Span: 分布式追踪的核心,记录调用链路
- Context: 确保 Span 在分布式系统中正确关联
- Resource: 描述生成遥测数据的实体
- Instrumentation: 添加可观测性代码的方式
- Exporter: 将数据发送到后端
- Processor: 处理 Span 的中间层
- Sampler: 控制采样率,平衡成本和观测性
- Metrics: 系统状态的数字测量
- Logs: 结构化日志,与 Trace 关联