Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a helper function to register all Collectors in a struct #712

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
115 changes: 115 additions & 0 deletions prometheus/examples_test.go
Expand Up @@ -265,6 +265,121 @@ func ExampleRegister() {
// taskCounterForWorker2001 registered.
}

func ExampleMustRegisterFields() {
s := struct {
PublicNumber int
secretNumber int
Cnt prometheus.Counter
Gge prometheus.GaugeFunc
CntVec *prometheus.CounterVec
secretCnt prometheus.Counter
}{
PublicNumber: 42,
secretNumber: 1971,
Cnt: prometheus.NewCounter(prometheus.CounterOpts{
Name: "public_number_changes_total",
Help: "Total number of changes of the public number.",
}),
CntVec: prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "ultimate_answers_total",
Help: "Total number of answers to ultimate questions.",
},
[]string{"category"},
),
// An unexported metric will not be registered.
secretCnt: prometheus.NewCounter(prometheus.CounterOpts{
Name: "secret_number_changes_total",
Help: "Total number of changes of the secret number.",
}),
}
s.Gge = prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "public_number_value",
Help: "Current value of the public number.",
},
func() float64 { return float64(s.PublicNumber) },
)
s.CntVec.WithLabelValues("life").Inc()
s.CntVec.WithLabelValues("the universe").Add(2)
s.CntVec.WithLabelValues("everything")

anotherGge := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "example",
Help: "Just another gauge to be registered along with the struct.",
})

reg := prometheus.NewRegistry()
prometheus.MustRegisterFieldsWith(reg, s, anotherGge)

metricFamilies, err := reg.Gather()
if err != nil {
panic("unexpected error during gathering")
}
for _, mf := range metricFamilies {
fmt.Println(proto.MarshalTextString(mf))
}

// Output:
// name: "example"
// help: "Just another gauge to be registered along with the struct."
// type: GAUGE
// metric: <
// gauge: <
// value: 0
// >
// >
//
// name: "public_number_changes_total"
// help: "Total number of changes of the public number."
// type: COUNTER
// metric: <
// counter: <
// value: 0
// >
// >
//
// name: "public_number_value"
// help: "Current value of the public number."
// type: GAUGE
// metric: <
// gauge: <
// value: 42
// >
// >
//
// name: "ultimate_answers_total"
// help: "Total number of answers to ultimate questions."
// type: COUNTER
// metric: <
// label: <
// name: "category"
// value: "everything"
// >
// counter: <
// value: 0
// >
// >
// metric: <
// label: <
// name: "category"
// value: "life"
// >
// counter: <
// value: 1
// >
// >
// metric: <
// label: <
// name: "category"
// value: "the universe"
// >
// counter: <
// value: 2
// >
// >
}

func ExampleSummary() {
temps := prometheus.NewSummary(prometheus.SummaryOpts{
Name: "pond_temperature_celsius",
Expand Down
50 changes: 50 additions & 0 deletions prometheus/registry.go
Expand Up @@ -19,6 +19,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"runtime"
"sort"
"strings"
Expand Down Expand Up @@ -55,6 +56,7 @@ var (
defaultRegistry = NewRegistry()
DefaultRegisterer Registerer = defaultRegistry
DefaultGatherer Gatherer = defaultRegistry
collectorType = reflect.TypeOf((*Collector)(nil)).Elem()
)

func init() {
Expand Down Expand Up @@ -177,6 +179,54 @@ func MustRegister(cs ...Collector) {
DefaultRegisterer.MustRegister(cs...)
}

// MustRegisterFields is a shortcut for
// MustRegisterFieldsWith(DefaultRegisterer, is...).
func MustRegisterFields(is ...interface{}) {
MustRegisterFieldsWith(DefaultRegisterer, is...)
}

// MustRegisterFieldsWith goes through the provided variadic arguments. Each
// argument that is implementing the Collector interface is registered with the
// provided Registerer. Each other argument must be a struct or a pointer to a
// struct and is checked for exported fields whose value implements the
// Collector interface. The value of each of those fields is registered with the
// provided Registerer.
//
// This function panics if one of the variadic arguments is neither a struct nor
// a pointer to a struct nor an implementation of the Collector interface. It
// also panics if any of the registrations fail.
//
// If Collectors are organized in a struct, this function allows to register
// them all in one call. Even in situations where only a subset of the fields of
// a struct are Collectors, this function might help, but remember that only
// exported fields will be registered.
func MustRegisterFieldsWith(r Registerer, is ...interface{}) {
for _, i := range is {
if maybeRegister(r, i) {
continue
}
v := reflect.Indirect(reflect.ValueOf(i))
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("not a struct: %#v", i))
}

for n := 0; n < v.NumField(); n++ {
f := v.Field(n)
if f.CanInterface() && f.Type().Implements(collectorType) {
maybeRegister(r, f.Interface().(Collector))
}
}
}
}

func maybeRegister(r Registerer, i interface{}) bool {
if c, ok := i.(Collector); ok {
r.MustRegister(c)
return true
}
return false
}

// Unregister removes the registration of the provided Collector from the
// DefaultRegisterer.
//
Expand Down