You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Golang编写的Prometheus Exporter中添加Histogram指标?

没问题!我来帮你把Histogram指标集成到你的Prometheus自定义Collector里,同时也会给你一个更简单的替代方案,看哪种更适合你的场景。

方式一:集成到自定义Collector中

如果需要保持现有自定义Collector的结构,我们需要手动处理Histogram的各个组成部分(桶计数、总和、样本数)——因为Prometheus的Histogram实际上是由多个关联的时间序列组成的。以下是修改后的完整代码:

package main

import (
    "fmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "net/http"
)

type fooCollector struct {
    fooMetric               *prometheus.Desc
    requestDurationBuckets  *prometheus.Desc
    requestDurationSum      *prometheus.Desc
    requestDurationCount    *prometheus.Desc
}

func newFooCollector(label1 string) *fooCollector {
    constLabels := prometheus.Labels{"env": label1}
    return &fooCollector{
        fooMetric: prometheus.NewDesc(
            "fff_metric",
            "Shows whether a foo has occurred in our cluster",
            nil,
            constLabels,
        ),
        requestDurationBuckets: prometheus.NewDesc(
            "request_duration_seconds_bucket",
            "Histogram of request durations in seconds",
            []string{"le"}, // 标记桶的上限值
            constLabels,
        ),
        requestDurationSum: prometheus.NewDesc(
            "request_duration_seconds_sum",
            "Sum of all request durations in seconds",
            nil,
            constLabels,
        ),
        requestDurationCount: prometheus.NewDesc(
            "request_duration_seconds_count",
            "Total number of requests recorded",
            nil,
            constLabels,
        ),
    }
}

func (collector *fooCollector) Describe(ch chan<- *prometheus.Desc) {
    // 注册所有指标的描述符
    ch <- collector.fooMetric
    ch <- collector.requestDurationBuckets
    ch <- collector.requestDurationSum
    ch <- collector.requestDurationCount
}

func (collector *fooCollector) Collect(ch chan<- prometheus.Metric) {
    // 推送原有的Gauge指标
    ch <- prometheus.MustNewConstMetric(collector.fooMetric, prometheus.GaugeValue, 111111)

    // 模拟请求时长观测数据(实际场景中替换为真实数据)
    observations := []float64{0.1, 0.2, 0.5, 0.8, 1.0, 1.5}
    // 使用Prometheus默认的桶范围,可根据业务需求自定义
    buckets := prometheus.DefBuckets

    // 计算桶计数、总和与样本数
    bucketCounts := make(map[float64]uint64)
    sum := 0.0
    count := uint64(len(observations))

    for _, obs := range observations {
        sum += obs
        // 为所有大于等于当前观测值的桶计数+1
        for _, le := range buckets {
            if obs <= le {
                bucketCounts[le]++
            }
        }
    }

    // 推送每个桶的计数指标
    for le, cnt := range bucketCounts {
        ch <- prometheus.MustNewConstMetric(
            collector.requestDurationBuckets,
            prometheus.CounterValue,
            float64(cnt),
            fmt.Sprintf("%v", le),
        )
    }

    // 推送总和与样本数指标
    ch <- prometheus.MustNewConstMetric(
        collector.requestDurationSum,
        prometheus.CounterValue,
        sum,
    )
    ch <- prometheus.MustNewConstMetric(
        collector.requestDurationCount,
        prometheus.CounterValue,
        float64(count),
    )
}

func main() {
    prometheus.MustRegister(newFooCollector("dev"))
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":80", nil)
}

关键修改说明:

  • 在fooCollector结构体中新增了Histogram所需的三个描述符,分别对应桶计数、观测值总和、样本数量
  • Describe方法需要将所有指标描述符传递给Prometheus,确保它能识别这些指标
  • Collect方法中模拟了观测数据,并手动计算桶的计数、总和与样本数,最后推送对应的Metric

方式二:直接使用HistogramVec(更简单)

如果你的场景不需要完全自定义收集逻辑,直接使用Prometheus官方提供的HistogramVec会更简洁,无需手动处理桶的计算:

package main

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
    "net/http"
    "time"
)

func main() {
    // 创建HistogramVec实例,可自定义桶范围与固定标签
    requestDuration := prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:        "request_duration_seconds",
            Help:        "Histogram of request durations in seconds",
            Buckets:     prometheus.DefBuckets, // 默认桶,可替换为自定义数组如[]float64{0.1, 0.5, 1, 2, 5}
            ConstLabels: prometheus.Labels{"env": "dev"},
        },
        []string{}, // 如需动态标签(如接口名称),可在此添加,例如[]string{"endpoint"}
    )

    // 注册指标到Prometheus
    prometheus.MustRegister(requestDuration)

    // 示例HTTP Handler,记录请求处理时长
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        // 模拟业务处理耗时
        time.Sleep(time.Millisecond * 150)
        // 记录请求时长
        duration := time.Since(start).Seconds()
        requestDuration.With(prometheus.Labels{}).Observe(duration)
        w.Write([]byte("Hello World!"))
    })

    // 暴露Metrics端点
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":80", nil)
}

优势说明:

  • 无需自定义Collector,直接调用Observe方法即可完成指标记录
  • Prometheus会自动处理桶计数、总和与样本数的计算
  • 支持动态标签,可轻松区分不同维度的观测数据(如不同API接口)

你可以根据自身需求选择合适的方案:如果需要和现有自定义Collector的逻辑深度整合,选择第一种方式;如果只是简单记录数值分布,第二种方式更高效易用。

内容的提问来源于stack exchange,提问作者Pengbo Wu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 17:42:32