add RegisterSet() and UnregisterSet() functions for registering the set in metrics export via global WritePrometheus() call

This commit is contained in:
Aliaksandr Valialkin 2022-08-08 17:13:46 +03:00
parent e9b4bb1534
commit c6c6640e5b
No known key found for this signature in database
GPG Key ID: A72BEC6CD3D0DED1
3 changed files with 72 additions and 2 deletions

View File

@ -14,6 +14,9 @@ package metrics
import ( import (
"io" "io"
"sort"
"sync"
"unsafe"
) )
type namedMetric struct { type namedMetric struct {
@ -27,7 +30,34 @@ type metric interface {
var defaultSet = NewSet() var defaultSet = NewSet()
// WritePrometheus writes all the registered metrics in Prometheus format to w. func init() {
RegisterSet(defaultSet)
}
var (
registeredSets = make(map[*Set]struct{})
registeredSetsLock sync.Mutex
)
// RegisterSet registers the given set s for metrics export via global WritePrometheus() call.
//
// See also UnregisterSet.
func RegisterSet(s *Set) {
registeredSetsLock.Lock()
registeredSets[s] = struct{}{}
registeredSetsLock.Unlock()
}
// UnregisterSet stops exporting metrics for the given s via global WritePrometheus() call.
func UnregisterSet(s *Set) {
registeredSetsLock.Lock()
delete(registeredSets, s)
registeredSetsLock.Unlock()
}
// WritePrometheus writes all the metrics from default set and all the registered sets in Prometheus format to w.
//
// Additional sets can be registered via RegisterSet() call.
// //
// If exposeProcessMetrics is true, then various `go_*` and `process_*` metrics // If exposeProcessMetrics is true, then various `go_*` and `process_*` metrics
// are exposed for the current process. // are exposed for the current process.
@ -39,7 +69,19 @@ var defaultSet = NewSet()
// }) // })
// //
func WritePrometheus(w io.Writer, exposeProcessMetrics bool) { func WritePrometheus(w io.Writer, exposeProcessMetrics bool) {
defaultSet.WritePrometheus(w) registeredSetsLock.Lock()
sets := make([]*Set, 0, len(registeredSets))
for s := range registeredSets {
sets = append(sets, s)
}
registeredSetsLock.Unlock()
sort.Slice(sets, func(i, j int) bool {
return uintptr(unsafe.Pointer(sets[i])) < uintptr(unsafe.Pointer(sets[j]))
})
for _, s := range sets {
s.WritePrometheus(w)
}
if exposeProcessMetrics { if exposeProcessMetrics {
WriteProcessMetrics(w) WriteProcessMetrics(w)
} }

View File

@ -3,10 +3,36 @@ package metrics
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"strings"
"testing" "testing"
"time" "time"
) )
func TestRegisterUnregisterSet(t *testing.T) {
const metricName = "metric_from_set"
const metricValue = 123
s := NewSet()
c := s.NewCounter(metricName)
c.Set(metricValue)
RegisterSet(s)
var bb bytes.Buffer
WritePrometheus(&bb, false)
data := bb.String()
expectedLine := fmt.Sprintf("%s %d\n", metricName, metricValue)
if !strings.Contains(data, expectedLine) {
t.Fatalf("missing %q in\n%s", expectedLine, data)
}
UnregisterSet(s)
bb.Reset()
WritePrometheus(&bb, false)
data = bb.String()
if strings.Contains(data, expectedLine) {
t.Fatalf("unepected %q in\n%s", expectedLine, data)
}
}
func TestInvalidName(t *testing.T) { func TestInvalidName(t *testing.T) {
f := func(name string) { f := func(name string) {
t.Helper() t.Helper()

2
set.go
View File

@ -22,6 +22,8 @@ type Set struct {
} }
// NewSet creates new set of metrics. // NewSet creates new set of metrics.
//
// Pass the set to RegisterSet() function in order to export its metrics via global WritePrometheus() call.
func NewSet() *Set { func NewSet() *Set {
return &Set{ return &Set{
m: make(map[string]*namedMetric), m: make(map[string]*namedMetric),