如何获取Prometheus指标的Help、Buckets等属性用于单元测试校验
Prometheus Histogram指标属性校验单元测试实现方法
你定义的HistogramVec类型指标,可以通过两种方式拿到Help、标签、Buckets、ConstLabels等配置做校验,注意Objectives、MaxAge是Summary类型指标的专属配置项,Histogram类型本身不存在这两个属性。
方法1:直接引用预存的Opts变量(最推荐,稳定性最高)
这是成本最低的实现方式,你在定义指标时,把配置项单独存为包内变量,测试时直接读取变量断言即可,不需要依赖Prometheus client的内部逻辑或者额外解析:
// 指标定义代码(业务侧) var ( requestDurationOpts = prometheus.HistogramOpts{ Name: "example_request_duration_seconds", Help: "Histogram for the runtime of a simple example function.", Buckets: prometheus.LinearBuckets(0.01, 0.01, 10), ConstLabels: prometheus.Labels{/* 你的固定标签 */}, } requestDurationLabels = []string{"label1"} RequestDuration = prometheus.NewHistogramVec(requestDurationOpts, requestDurationLabels) )
单元测试里直接断言配置值即可:
func TestRequestDurationConfig(t *testing.T) { // 校验Help assert.Equal(t, "Histogram for the runtime of a simple example function.", requestDurationOpts.Help) // 校验Buckets assert.Equal(t, prometheus.LinearBuckets(0.01, 0.01, 10), requestDurationOpts.Buckets) // 校验可变标签 assert.Equal(t, []string{"label1"}, requestDurationLabels) // 校验ConstLabels assert.Equal(t, prometheus.Labels{/* 预期固定标签 */}, requestDurationOpts.ConstLabels) }
如果是Summary类型指标,直接读SummaryOpts里的Objectives、MaxAge、AgeBuckets字段做断言即可。
方法2:通过Metric收集接口解析元信息(无法修改业务代码时使用)
如果你不能修改指标定义的代码、没法直接拿到Opts变量,可以通过Prometheus Collector标准的Collect接口拉取指标的Protobuf结构,从结构里读取所有配置:
import ( "testing" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" ) func TestRequestDurationConfigByCollect(t *testing.T) { // 初始化待校验的指标 requestDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{ Name: "example_request_duration_seconds", Help: "Histogram for the runtime of a simple example function.", Buckets: prometheus.LinearBuckets(0.01, 0.01, 10), }, []string{"label1"}) // 必须先初始化一个标签组合的指标实例,否则Collect拿不到数据 requestDuration.With(prometheus.Labels{"label1": "test"}) // 收集指标数据 metricCh := make(chan prometheus.Metric, 1) requestDuration.Collect(metricCh) metric := <-metricCh close(metricCh) // 写入MetricFamily结构体 mf := &dto.MetricFamily{} err := metric.Write(mf) assert.NoError(t, err) // 校验Help、指标名、指标类型 assert.Equal(t, "example_request_duration_seconds", mf.GetName()) assert.Equal(t, "Histogram for the runtime of a simple example function.", mf.GetHelp()) assert.Equal(t, dto.MetricType_HISTOGRAM, mf.GetType()) // 校验Buckets expectedBuckets := prometheus.LinearBuckets(0.01, 0.01, 10) actualBuckets := make([]float64, 0, len(expectedBuckets)) for _, b := range mf.Metric[0].GetHistogram().GetBucket() { actualBuckets = append(actualBuckets, b.GetUpperBound()) } assert.Equal(t, expectedBuckets, actualBuckets) // 校验标签、ConstLabels actualLabels := make([]string, 0) actualConstLabels := make(prometheus.Labels) for _, lp := range mf.Metric[0].GetLabel() { actualLabels = append(actualLabels, lp.GetName()) // 非可变标签就是ConstLabels if lp.GetName() != "label1" { actualConstLabels[lp.GetName()] = lp.GetValue() } } assert.Contains(t, actualLabels, "label1") // 没有配置ConstLabels就断言为空,有配置就填入预期值做对比 assert.Empty(t, actualConstLabels) }
注意:不要用反射读取
HistogramVec的未导出字段,不同版本的client_golang内部字段结构可能调整,会导致测试用例兼容性很差。
内容的提问来源于stack exchange,提问作者9gagger
相关产品推荐
相关产品推荐

