如何在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
相关产品推荐
相关产品推荐

