Prometheus 代码埋点(Instrumentation)最佳实践

本文档来自 Prometheus 官方文档 - Instrumentation,提供在应用程序和服务中进行指标埋点的指导原则和具体建议。


一、总体原则

  • “埋点一切”:每个库、子系统和服务都应至少包含几个基础指标,以反映其运行状态。
  • 埋点应内置于代码中:在使用指标的同一文件中实例化指标对象,便于从告警 → 控制台 → 代码快速定位问题。
  • 日志与指标联动:每有一行日志输出,就应有一个对应的计数器(counter)。这有助于量化错误频率和持续时间。

二、按服务类型分类的埋点策略

1. 在线服务型(Online-serving)

用户或系统期待立即响应(如 HTTP 服务、数据库)。

关键指标

  • 请求总数(requests_total
  • 错误数(errors_total,按错误类型打标签)
  • 延迟(使用 Histogram 或 Summary)
  • 正在处理的请求数(Gauge,in_flight_requests

建议

  • 客户端与服务端均需埋点,便于对比排查问题。
  • 统一在请求结束时计数(便于与错误、延迟对齐)。

2. 离线处理型(Offline-processing)

无实时响应要求,常涉及批处理或多阶段流水线。

关键指标(每阶段)

  • 输入项数量
  • 处理中项数量(Gauge)
  • 最后处理时间戳(Unix 时间,非“距今多久”)
  • 输出项数量
  • 若使用批处理,还需记录批次输入/输出量

高级技巧

  • 引入心跳机制:注入带时间戳的虚拟任务贯穿整个流水线,各阶段上报最新看到的心跳时间,可追踪端到端延迟。
  • 对于无空闲期的系统,可省略显式心跳。

3. 批处理作业(Batch jobs)

非持续运行,难以通过 Pull 模式采集。

关键指标(应推送到 Pushgateway)

  • 上次成功完成时间戳(last_success_timestamp_seconds
  • 总运行时间(Gauge)
  • 各主要阶段耗时(Gauge)
  • 上次完成时间(无论成功与否)
  • 作业特有统计量(如处理记录总数)

补充建议

  • 若作业运行时间 > 几分钟,同时支持 Pull 模式(如启动 HTTP 端点),以便监控资源使用、外部调用延迟等。
  • 若作业频率很高(如 <15 分钟一次),考虑改造成守护进程(daemon),按离线处理模式监控。

三、子系统与库的埋点

  • 库应开箱即用:无需用户额外配置即可自动暴露指标。

  • 对外部资源访问的库(如网络、磁盘、IPC):

    • 必须包含:调用次数、错误数、延迟(Histogram/Summary)
  • 内部复杂库:可增加内部错误、延迟及通用统计指标。

  • 标签区分使用场景:

    • ✅ 数据库连接池:用 database="db1" 区分不同数据库
  • ❌ DNS 客户端库:无需区分不同调用者


四、通用组件埋点建议

错误处理

  • 每次失败都应递增一个计数器。
  • 同时提供总尝试次数指标,便于计算失败率。

线程池(Threadpool)

  • 队列中请求数(Gauge)
  • 正在使用的线程数(Gauge)
  • 总线程数(Gauge)
  • 已处理任务数(Counter)
  • 任务处理耗时(Histogram)
  • 任务在队列中等待时间(Histogram)

缓存(Cache)

  • 总查询数(cache_requests_total
  • 命中数(cache_hits_total
  • 缓存操作延迟(Histogram)
  • 后端系统指标:缓存所代理的原始服务的请求、错误、延迟也应保留

自定义 Collector

  • 导出采集耗时(Gauge,单位秒)

  • 导出采集过程中遇到的错误数(Counter)

    ⚠️ 这是少数允许用 Gauge 表示“持续时间”的场景(另一场景是批处理作业耗时),因为它们描述的是单次抓取/推送行为,而非长期分布。


五、技术注意事项

标签(Labels)使用

  • 合并同类项:多个相似指标应合并为一个带标签的指标。
    http_responses_total{code="200"}{code="404"}
    http_responses_200_totalhttp_responses_404_total

  • 避免动态生成指标名:所有变化维度应使用标签表示(代理其他监控系统除外)。

  • 警惕高基数:

    • 单个指标的标签组合(labelset)应尽量控制在 <10 种
    • 全局高基数指标(>100)应严格限制数量
    • 示例:10,000 节点 × 每节点 10 个文件系统 = 10 万时间序列(可接受);若再 × 10,000 用户 = 1 亿(不可接受)

指标类型选择

类型 特点 使用场景
Counter 只增(可重置) 事件累计数(请求、错误、字节)
Gauge 可增可减 瞬时状态(内存、温度、进行中请求数)
Histogram / Summary 分布统计 延迟、响应大小等

📌 规则:值能下降 → 用 Gauge;否则用 Counter。

时间戳 vs. 持续时间

  • 导出事件发生的时间戳(Unix 秒),而非“距今多少秒”。

    • 例如:last_success_timestamp_seconds
    • 查询时用 time() - last_success_timestamp_seconds 计算间隔
    • 优点:避免更新逻辑卡死导致指标失效

性能敏感代码

  • 高频调用路径(>10 万次/秒)需谨慎:
    • 减少指标更新次数
    • 避免在循环内创建带标签的指标(应缓存 With()labels() 结果)
    • 注意获取系统时间可能涉及 syscall,有开销

避免缺失指标

  • 已知可能存在的指标,即使初始值为 0 也应暴露。
  • 多数客户端库(Go/Java/Python)会自动为无标签指标注册 0 初始值。

💡 核心思想:良好的埋点设计是可观测性的基石。合理的指标命名、类型选择和标签使用,能极大提升故障排查效率和系统可维护性。