1. 课程导论:为什么需要自定义指标?OpenTelemetry 指标体系概览
1.1 为什么标准指标不够用?
在可观测性实践中,OpenTelemetry 提供了丰富的自动插桩(Auto-Instrumentation)能力,能够捕获 HTTP 请求量、数据库调用耗时、gRPC 错误率等通用指标。然而,业务逻辑的独特性决定了标准指标无法覆盖所有场景:
- 业务语义缺失:标准指标只能告诉你“请求数”,无法区分“支付成功”与“支付失败”。
- 维度不足:自动埋点通常只包含 HTTP 方法、状态码等通用标签,缺少
user_tier、region、product_id等业务维度。 - 聚合粒度不匹配:标准 Histogram 的桶(Bucket)分布面向通用延迟,无法针对特定 SLA(如 99 分位 < 200ms)优化。
- 无法表达业务状态:队列积压长度、并发在线用户数、库存水位等瞬时值,标准指标无法直接提供。
核心结论:自定义指标是连接基础设施可观测性与业务可观测性的桥梁。没有自定义指标,你只能看到“系统在运行”,却看不到“业务在发生什么”。
1.2 OpenTelemetry 指标体系概览
OpenTelemetry 定义了四种基础指标类型(Metric Kind),每种类型对应不同的语义和聚合行为。理解它们的差异是设计自定义指标口径的第一步。
| 指标类型 | 英文名称 | 语义 | 典型场景 | 聚合方式 |
|---|---|---|---|---|
| Counter | Counter | 单调递增的累计值 | 请求总数、错误总数、订单创建数 | Sum / Rate |
| UpDownCounter | UpDownCounter | 可增可减的有符号累计值 | 活跃连接数、队列长度、库存余量 | Sum / Delta |
| Histogram | Histogram | 数值分布采样,按桶统计 | 请求延迟、响应体大小、批处理耗时 | 分位数 / 桶计数 / Sum |
| Gauge | Gauge | 可上可下的瞬时快照值 | CPU 使用率、内存占用、当前温度 | LastValue / Min / Max |
记忆口诀:Counter 只增不减,UpDownCounter 可增可减,Histogram 看分布,Gauge 看快照。
1.3 四种指标类型的核心区别
选择错误的指标类型会导致数据失真或查询困难。以下从三个关键维度对比:
- 单调性:Counter 强制单调递增,UpDownCounter 和 Gauge 允许双向变化。
- 聚合语义:Counter 和 UpDownCounter 适合做 Sum 聚合;Histogram 适合做分位数估算;Gauge 适合做 LastValue 展示。
- 导出行为:Counter 在 Prometheus 中通常配合
rate()使用;Histogram 自带_bucket、_count、_sum后缀;Gauge 直接暴露当前值。
// 伪代码示例:四种指标的定义差异
// Counter:只允许 Add(正数)
counter.Add(1, attribute.String("status", "success"))
// UpDownCounter:允许 Add(正数或负数)
upDownCounter.Add(1) // 连接建立
upDownCounter.Add(-1) // 连接关闭
// Histogram:记录单个观测值
histogram.Record(152.3, attribute.String("endpoint", "/checkout"))
// Gauge:设置当前值
gauge.Set(42.0, attribute.String("pool", "worker"))
1.4 指标类型选择决策树
下面这张 SVG 图帮助你快速判断应该使用哪种指标类型:
1.5 自定义指标的设计原则
在开始编码之前,记住三条黄金法则:
- 口径先行:先定义指标的名称、类型、单位、标签集合,再写代码。口径文档比代码更持久。
- 标签有界:避免使用
user_id、request_id等无限基数(Cardinality)的标签,否则会导致存储爆炸和查询性能崩溃。 - 语义一致:同一业务含义的指标,在不同服务中保持相同的命名和标签结构。例如“订单创建数”统一使用
orders.created,不要一个服务用order_create_count,另一个用create_order_total。
常见陷阱:不要将 Histogram 当作 Gauge 使用。例如用 Histogram 记录“当前队列长度”是错误的,因为 Histogram 记录的是观测值的分布,而不是瞬时状态。队列长度应该使用 UpDownCounter 或 Gauge。