Prometheus 的 Golang SDK 中,使用 promauto.NewCounterVec
类函数时,会自动把指标注册进默认 Registerer。如果两个指标重复注册,会报错 panic:
// a.go
var (
dbPingCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "db_ping",
}, []string{
"dbname",
"result",
})
)
// b.go
var (
dbPingCounter = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "db_ping",
}, []string{
"dbname",
"result",
})
)
// 如果 b.go 晚于 a.go 被 import,运行 promauto.NewCounterVec 时会 panic
遇到这个场景的原因是,我要将一个包的子包单独拆分出来;由于包的用户在迁移过程可能同时用新旧两个包,而两个包中有同样的指标(name 及 labels 一致),会遇到上述的问题。
一个解决办法是,不用 proauto
,自行将 Collector 注册进 Register,并判断返回的错误:
func TryRegisterPromCounterVec(collector *prometheus.CounterVec) (*prometheus.CounterVec, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector *prometheus.CounterVec
)
if existingCollector, castOK = are.ExistingCollector.(*prometheus.CounterVec); !castOK {
return nil, fmt.Errorf("collector already registered but it's not *prometheus.CounterVec")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromCounter(collector prometheus.Counter) (prometheus.Counter, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector prometheus.Counter
)
if existingCollector, castOK = are.ExistingCollector.(prometheus.Counter); !castOK {
return nil, fmt.Errorf("collector already registered but it's not prometheus.Counter")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromGaugeVec(collector *prometheus.GaugeVec) (*prometheus.GaugeVec, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector *prometheus.GaugeVec
)
if existingCollector, castOK = are.ExistingCollector.(*prometheus.GaugeVec); !castOK {
return nil, fmt.Errorf("collector already registered but it's not *prometheus.GaugeVec")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromGauge(collector prometheus.Gauge) (prometheus.Gauge, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector prometheus.Gauge
)
if existingCollector, castOK = are.ExistingCollector.(prometheus.Gauge); !castOK {
return nil, fmt.Errorf("collector already registered but it's not prometheus.Gauge")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromHistogramVec(collector *prometheus.HistogramVec) (*prometheus.HistogramVec, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector *prometheus.HistogramVec
)
if existingCollector, castOK = are.ExistingCollector.(*prometheus.HistogramVec); !castOK {
return nil, fmt.Errorf("collector already registered but it's not *prometheus.HistogramVec")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromHistogram(collector prometheus.Histogram) (prometheus.Histogram, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector prometheus.Histogram
)
if existingCollector, castOK = are.ExistingCollector.(prometheus.Histogram); !castOK {
return nil, fmt.Errorf("collector already registered but it's not prometheus.Histogram")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromSummaryVec(collector *prometheus.SummaryVec) (*prometheus.SummaryVec, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector *prometheus.SummaryVec
)
if existingCollector, castOK = are.ExistingCollector.(*prometheus.SummaryVec); !castOK {
return nil, fmt.Errorf("collector already registered but it's not *prometheus.SummaryVec")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}
func TryRegisterPromSummary(collector prometheus.Summary) (prometheus.Summary, error) {
if err := prometheus.Register(collector); err != nil {
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
var (
castOK bool
existingCollector prometheus.Summary
)
if existingCollector, castOK = are.ExistingCollector.(prometheus.Summary); !castOK {
return nil, fmt.Errorf("collector already registered but it's not prometheus.Summary")
}
return existingCollector, nil
}
return nil, fmt.Errorf("register collector failed: %w", err)
}
return collector, nil
}