Added GetOrCreateGauge to be consistent with Counter and Summary

This commit is contained in:
Aliaksandr Valialkin 2019-04-15 15:34:02 +03:00
parent d8d17da673
commit 6cd90960e1
4 changed files with 90 additions and 0 deletions

View File

@ -19,6 +19,9 @@ import (
// //
// The returned gauge is safe to use from concurrent goroutines. // The returned gauge is safe to use from concurrent goroutines.
func NewGauge(name string, f func() float64) *Gauge { func NewGauge(name string, f func() float64) *Gauge {
if f == nil {
panic(fmt.Errorf("BUG: f cannot be nil"))
}
g := &Gauge{ g := &Gauge{
f: f, f: f,
} }
@ -47,3 +50,51 @@ func (g *Gauge) marshalTo(prefix string, w io.Writer) {
fmt.Fprintf(w, "%s %g\n", prefix, v) fmt.Fprintf(w, "%s %g\n", prefix, v)
} }
} }
// GetOrCreateGauge returns registered gauge with the given name
// or creates new gauge if the registry doesn't contain gauge with
// the given name.
//
// name must be valid Prometheus-compatible metric with possible lables.
// For instance,
//
// * foo
// * foo{bar="baz"}
// * foo{bar="baz",aaa="b"}
//
// The returned gauge is safe to use from concurrent goroutines.
//
// Performance tip: prefer NewGauge instead of GetOrCreateGauge.
func GetOrCreateGauge(name string, f func() float64) *Gauge {
metricsMapLock.Lock()
nm := metricsMap[name]
metricsMapLock.Unlock()
if nm == nil {
// Slow path - create and register missing gauge.
if f == nil {
panic(fmt.Errorf("BUG: f cannot be nil"))
}
if err := validateMetric(name); err != nil {
panic(fmt.Errorf("BUG: invalid metric name %q: %s", name, err))
}
nmNew := &namedMetric{
name: name,
metric: &Gauge{
f: f,
},
}
metricsMapLock.Lock()
nm = metricsMap[name]
if nm == nil {
nm = nmNew
metricsMap[name] = nm
metricsList = append(metricsList, nm)
}
metricsMapLock.Unlock()
}
g, ok := nm.metric.(*Gauge)
if !ok {
panic(fmt.Errorf("BUG: metric %q isn't a Gauge. It is %T", name, nm.metric))
}
return g
}

View File

@ -16,3 +16,26 @@ func ExampleGauge() {
// Obtain gauge value. // Obtain gauge value.
fmt.Println(g.Get()) fmt.Println(g.Get())
} }
func ExampleGauge_vec() {
for i := 0; i < 3; i++ {
// Dynamically construct metric name and pass it to GetOrCreateGauge.
name := fmt.Sprintf(`metric{label1=%q, label2="%d"}`, "value1", i)
iLocal := i
metrics.GetOrCreateGauge(name, func() float64 {
return float64(iLocal + 1)
})
}
// Read counter values.
for i := 0; i < 3; i++ {
name := fmt.Sprintf(`metric{label1=%q, label2="%d"}`, "value1", i)
n := metrics.GetOrCreateGauge(name, func() float64 { return 0 }).Get()
fmt.Println(n)
}
// Output:
// 1
// 2
// 3
}

View File

@ -6,6 +6,15 @@ import (
"testing" "testing"
) )
func TestGaugeError(t *testing.T) {
expectPanic(t, "NewGauge_nil_callback", func() {
NewGauge("NewGauge_nil_callback", nil)
})
expectPanic(t, "GetOrCreateGauge_nil_callback", func() {
GetOrCreateGauge("GetOrCreateGauge_nil_callback", nil)
})
}
func TestGaugeSerial(t *testing.T) { func TestGaugeSerial(t *testing.T) {
name := "GaugeSerial" name := "GaugeSerial"
n := 1.23 n := 1.23

View File

@ -14,6 +14,7 @@ func TestInvalidName(t *testing.T) {
expectPanic(t, fmt.Sprintf("NewGauge(%q)", name), func() { NewGauge(name, func() float64 { return 0 }) }) expectPanic(t, fmt.Sprintf("NewGauge(%q)", name), func() { NewGauge(name, func() float64 { return 0 }) })
expectPanic(t, fmt.Sprintf("NewSummary(%q)", name), func() { NewSummary(name) }) expectPanic(t, fmt.Sprintf("NewSummary(%q)", name), func() { NewSummary(name) })
expectPanic(t, fmt.Sprintf("GetOrCreateCounter(%q)", name), func() { GetOrCreateCounter(name) }) expectPanic(t, fmt.Sprintf("GetOrCreateCounter(%q)", name), func() { GetOrCreateCounter(name) })
expectPanic(t, fmt.Sprintf("GetOrCreateGauge(%q)", name), func() { GetOrCreateGauge(name, func() float64 { return 0 }) })
expectPanic(t, fmt.Sprintf("GetOrCreateSummary(%q)", name), func() { GetOrCreateSummary(name) }) expectPanic(t, fmt.Sprintf("GetOrCreateSummary(%q)", name), func() { GetOrCreateSummary(name) })
} }
f("") f("")
@ -52,6 +53,12 @@ func TestGetOrCreateNotCounter(t *testing.T) {
expectPanic(t, name, func() { GetOrCreateCounter(name) }) expectPanic(t, name, func() { GetOrCreateCounter(name) })
} }
func TestGetOrCreateNotGauge(t *testing.T) {
name := "GetOrCreateNotGauge"
NewCounter(name)
expectPanic(t, name, func() { GetOrCreateGauge(name, func() float64 { return 0 }) })
}
func TestGetOrCreateNotSummary(t *testing.T) { func TestGetOrCreateNotSummary(t *testing.T) {
name := "GetOrCreateNotSummary" name := "GetOrCreateNotSummary"
NewCounter(name) NewCounter(name)