diff --git a/prometheus/collector.go b/prometheus/collector.go index adc07b1..5492ed3 100644 --- a/prometheus/collector.go +++ b/prometheus/collector.go @@ -15,15 +15,15 @@ package prometheus // Collector is the interface implemented by anything that can be used by // Prometheus to collect metrics. A Collector has to be registered for -// collection. See Register, MustRegister, RegisterOrGet, and MustRegisterOrGet. +// collection. See Registerer.Register. // -// The stock metrics provided by this package (like Gauge, Counter, Summary) are -// also Collectors (which only ever collect one metric, namely itself). An -// implementer of Collector may, however, collect multiple metrics in a -// coordinated fashion and/or create metrics on the fly. Examples for collectors -// already implemented in this library are the metric vectors (i.e. collection -// of multiple instances of the same Metric but with different label values) -// like GaugeVec or SummaryVec, and the ExpvarCollector. +// The stock metrics provided by this package (Gauge, Counter, Summary, +// Histogram, Untyped) are also Collectors (which only ever collect one metric, +// namely itself). An implementer of Collector may, however, collect multiple +// metrics in a coordinated fashion and/or create metrics on the fly. Examples +// for collectors already implemented in this library are the metric vectors +// (i.e. collection of multiple instances of the same Metric but with different +// label values) like GaugeVec or SummaryVec, and the ExpvarCollector. type Collector interface { // Describe sends the super-set of all possible descriptors of metrics // collected by this Collector to the provided channel and returns once diff --git a/prometheus/doc.go b/prometheus/doc.go index b0e384e..ce93443 100644 --- a/prometheus/doc.go +++ b/prometheus/doc.go @@ -141,10 +141,10 @@ // // So far, everything we did operated on the so-called default registry, as it // can be found in the global DefaultRegistry variable. With NewRegistry, you -// can create a custom registry, or you can even implement the Registry -// interface yourself. The methods Register and Unregister work in the same way -// on a custom registry as the global functions Register and Unregister on the -// default registry. +// can create a custom registry, or you can even implement the Registerer or +// Deliverer interfaces yourself. The methods Register and Unregister work in +// the same way on a custom registry as the global functions Register and +// Unregister on the default registry. // // There are a number of uses for custom registries: You can use registries // with special properties, see NewPedanticRegistry. You can avoid global state, @@ -160,15 +160,15 @@ // HTTP Exposition // // The Handler function used so far to get an http.Handler for serving the -// metrics is also acting on the DefaultRegistry. With HondlerFor, you can -// create a handler for a custom registry. It also allows to create handler that -// act differently on errors or allow to log errors. Also note that the handler -// returned by the Handler function is already instrumented with some HTTP -// metrics. You can call UninstrumentedHandler to get a handler for the -// DefaultRegistry that is not instrumented, or you can use InstrumentHandler to -// instrument any http.Handlers of your choice. (But note that the way the -// instrumentation happens is partially obsolete. Better ways are being worked -// on.) +// metrics is also acting on the DefaultRegistry. With HandlerFor, you can +// create a handler for a custom registry or anything the implements the +// Deliverer interface. It also allows to create handler that act differently on +// errors or allow to log errors. Also note that the handler returned by the +// Handler function is already instrumented with some HTTP metrics. You can call +// UninstrumentedHandler to get a handler for the DefaultRegistry that is not +// instrumented, or you can use InstrumentHandler to instrument any +// http.Handlers of your choice. (But note that the way the instrumentation +// happens is partially obsolete. Better ways are being worked on.) // // Pushing to the Pushgateway // diff --git a/prometheus/example_clustermanager_test.go b/prometheus/example_clustermanager_test.go index 3c263e6..581f922 100644 --- a/prometheus/example_clustermanager_test.go +++ b/prometheus/example_clustermanager_test.go @@ -113,6 +113,6 @@ func ExampleCollector_clustermanager() { // Since we are dealing with custom Collector implementations, it might // be a good idea to try it out with a pedantic registry. reg := prometheus.NewPedanticRegistry() - prometheus.MustRegisterWith(reg, workerDB) - prometheus.MustRegisterWith(reg, workerCA) + reg.MustRegister(workerDB) + reg.MustRegister(workerCA) } diff --git a/prometheus/examples_test.go b/prometheus/examples_test.go index 819c8e4..188e9fb 100644 --- a/prometheus/examples_test.go +++ b/prometheus/examples_test.go @@ -388,9 +388,9 @@ func ExampleSummaryVec() { // by registering it with a custom registry and then let it collect the // metrics. reg := prometheus.NewRegistry() - prometheus.MustRegisterWith(reg, temps) + reg.MustRegister(temps) - metricFamilies, err := reg.Collect() + metricFamilies, err := reg.Deliver() if err != nil || len(metricFamilies) != 1 { panic("unexpected behavior of custom test registry") } diff --git a/prometheus/http.go b/prometheus/http.go index f310925..41d1bd8 100644 --- a/prometheus/http.go +++ b/prometheus/http.go @@ -65,12 +65,12 @@ func UninstrumentedHandler() http.Handler { return HandlerFor(DefaultRegistry, HandlerOpts{}) } -// HandlerFor returns an http.Handler for the provided registry. The behavior ef +// HandlerFor returns an http.Handler for the provided Deliverer. The behavior ef // the Handler is defined by the provided HandlerOpts. The Handler is NOT // instrumented with InstrumentHandler. -func HandlerFor(r Registry, opts HandlerOpts) http.Handler { +func HandlerFor(reg Deliverer, opts HandlerOpts) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - mfs, err := r.Collect() + mfs, err := reg.Deliver() if err != nil { if opts.ErrorLog != nil { opts.ErrorLog.Println("error collecting metrics:", err) diff --git a/prometheus/http_test.go b/prometheus/http_test.go index 71a1379..d4bfe25 100644 --- a/prometheus/http_test.go +++ b/prometheus/http_test.go @@ -146,7 +146,7 @@ func TestHandlerErrorHandling(t *testing.T) { Name: "the_count", Help: "Ah-ah-ah! Thunder and lightning!", }) - MustRegisterWith(reg, cnt) + reg.MustRegister(cnt) cntVec := NewCounterVec( CounterOpts{ @@ -158,9 +158,9 @@ func TestHandlerErrorHandling(t *testing.T) { ) cntVec.WithLabelValues("val1").Inc() cntVec.WithLabelValues("val2").Inc() - MustRegisterWith(reg, cntVec) + reg.MustRegister(cntVec) - MustRegisterWith(reg, errorCollector{}) + reg.MustRegister(errorCollector{}) logBuf := &bytes.Buffer{} logger := log.New(logBuf, "", 0) diff --git a/prometheus/process_collector_test.go b/prometheus/process_collector_test.go index 6cae6e8..3bc08b1 100644 --- a/prometheus/process_collector_test.go +++ b/prometheus/process_collector_test.go @@ -25,7 +25,7 @@ func TestProcessCollector(t *testing.T) { t.Fatal(err) } - mfs, err := registry.Collect() + mfs, err := registry.Deliver() if err != nil { t.Fatal(err) } diff --git a/prometheus/push/push.go b/prometheus/push/push.go index 8b0351f..a65e792 100644 --- a/prometheus/push/push.go +++ b/prometheus/push/push.go @@ -44,8 +44,8 @@ import ( const contentTypeHeader = "Content-Type" -// Registry triggers a metric collection by the provided registry and pushes all -// collected metrics to the Pushgateway specified by url, using the provided job +// Registry triggers a metric collection by the provided Deliverer and pushes all +// delivered metrics to the Pushgateway specified by url, using the provided job // name and the (optional) further grouping labels (the grouping map may be // nil). See the Pushgateway documentation for detailed implications of the job // and other grouping labels. Neither the job name nor any grouping label value @@ -59,18 +59,18 @@ const contentTypeHeader = "Content-Type" // Note that all previously pushed metrics with the same job and other grouping // labels will be replaced with the metrics pushed by this call. (It uses HTTP // method 'PUT' to push to the Pushgateway.) -func Registry(r prometheus.Registry, job string, grouping map[string]string, url string) error { - return push(r, job, grouping, url, "PUT") +func Registry(reg prometheus.Deliverer, job string, grouping map[string]string, url string) error { + return push(reg, job, grouping, url, "PUT") } // RegistryAdd works like Registry, but only previously pushed metrics with the // same name (and the same job and other grouping labels) will be replaced. (It // uses HTTP method 'POST' to push to the Pushgateway.) -func RegistryAdd(r prometheus.Registry, job string, grouping map[string]string, url string) error { - return push(r, job, grouping, url, "POST") +func RegistryAdd(reg prometheus.Deliverer, job string, grouping map[string]string, url string) error { + return push(reg, job, grouping, url, "POST") } -func push(r prometheus.Registry, job string, grouping map[string]string, pushURL, method string) error { +func push(reg prometheus.Deliverer, job string, grouping map[string]string, pushURL, method string) error { if !strings.Contains(pushURL, "://") { pushURL = "http://" + pushURL } @@ -93,7 +93,7 @@ func push(r prometheus.Registry, job string, grouping map[string]string, pushURL } pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/")) - mfs, err := r.Collect() + mfs, err := reg.Deliver() if err != nil { return err } @@ -133,15 +133,15 @@ func push(r prometheus.Registry, job string, grouping map[string]string, pushURL return nil } -// Collectors works like Registry, but it does not collect via a -// registry. Instead, it collects from the provided collectors directly. It is a -// convenient way to push only a few metrics. +// Collectors works like Registry, but it does not use a Deliverer. Instead, it +// collects from the provided collectors directly. It is a convenient way to +// push only a few metrics. func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error { return pushCollectors(job, grouping, url, "PUT", collectors...) } -// AddCollectors works like PushAdd, but it does not collect via a -// registry. Instead, it collects from the provided collectors directly. It is a +// AddCollectors works like RegistryAdd, but it does not use a Deliverer. +// Instead, it collects from the provided collectors directly. It is a // convenient way to push only a few metrics. func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error { return pushCollectors(job, grouping, url, "POST", collectors...) diff --git a/prometheus/push/push_test.go b/prometheus/push/push_test.go index 86630ce..dde7b55 100644 --- a/prometheus/push/push_test.go +++ b/prometheus/push/push_test.go @@ -80,10 +80,10 @@ func TestPush(t *testing.T) { }) reg := prometheus.NewRegistry() - prometheus.MustRegisterWith(reg, metric1) - prometheus.MustRegisterWith(reg, metric2) + reg.MustRegister(metric1) + reg.MustRegister(metric2) - mfs, err := reg.Collect() + mfs, err := reg.Deliver() if err != nil { t.Fatal(err) } diff --git a/prometheus/registry.go b/prometheus/registry.go index dc0144f..1d1b57d 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -44,8 +44,8 @@ func init() { // NewRegistry creates a new vanilla Registry without any Collectors // pre-registered. -func NewRegistry() Registry { - return ®istry{ +func NewRegistry() *Registry { + return &Registry{ collectorsByID: map[uint64]Collector{}, descIDs: map[uint64]struct{}{}, dimHashesByName: map[string]uint64{}, @@ -62,14 +62,18 @@ func NewRegistry() Registry { // Collector. Well-behaved Collectors and Metrics will only provide consistent // Descs. This Registry is useful to test the implementation of Collectors and // Metrics. -func NewPedanticRegistry() Registry { - r := NewRegistry().(*registry) +func NewPedanticRegistry() *Registry { + r := NewRegistry() r.pedanticChecksEnabled = true return r } -// Registry is the interface for the metrics registry. -type Registry interface { +// Registerer is the interface for the part of a registry in charge of +// registering and unregistering. Users of custom registries should use +// Registerer as type for registration purposes (rather then Registry). In that +// way, they are free to exchange the Registerer implementation (e.g. for +// testing purposes). +type Registerer interface { // Register registers a new Collector to be included in metrics // collection. It returns an error if the descriptors provided by the // Collector are invalid or if they - in combination with descriptors of @@ -84,6 +88,10 @@ type Registry interface { // It is in general not safe to register the same Collector multiple // times concurrently. Register(Collector) error + // MustRegister works like Register but registers any number of + // Collectors and panics upon the first registration that causes an + // error. + MustRegister(...Collector) // Unregister unregisters the Collector that equals the Collector passed // in as an argument. (Two Collectors are considered equal if their // Describe method yields the same set of descriptors.) The function @@ -96,24 +104,14 @@ type Registry interface { // instance must only collect consistent metrics throughout its // lifetime. Unregister(Collector) bool - // SetInjectionHook sets the provided hook to inject MetricFamilies. The - // hook is a function that is called whenever metrics are collected. The - // MetricFamily protobufs returned by the hook function are merged with - // the metrics collected in the usual way. - // - // This is a way to directly inject MetricFamily protobufs managed and - // owned by the caller. The caller has full responsibility. As no - // registration of the injected metrics has happened, there was no check - // at registration time. If the injection results in inconsistent - // metrics, the Collect call will return an error. Some problems may - // even go undetected, like invalid label names in the injected - // protobufs. - // - // The hook function must be callable at any time and concurrently. - SetInjectionHook(hook func() []*dto.MetricFamily) - // Collect collects metrics from registered Collectors and returns them +} + +// Deliverer is the interface for the part of a registry in charge of delivering +// the collected metrics. +type Deliverer interface { + // Deliver collects metrics from registered Collectors and returns them // as lexicographically sorted MetricFamily protobufs. Even if an error - // occurs, Collect attempts to collect as many metrics as + // occurs, Deliver attempts to collect as many metrics as // possible. Hence, if a non-nil error is returned, the returned // MetricFamily slice could be nil (in case of a fatal error that // prevented any meaningful metric collection) or contain a number of @@ -124,19 +122,7 @@ type Registry interface { // duplicate metrics, no invalid identifiers). In scenarios where // complete collection is critical, the returned MetricFamily protobufs // should be disregarded if the returned error is non-nil. - Collect() ([]*dto.MetricFamily, error) -} - -// MustRegisterWith registers the provided Collectors with the provided Registry -// and panics upon the first registration that causes an error. -// -// See Registry.Register for more details of Collector registration. -func MustRegisterWith(r Registry, cs ...Collector) { - for _, c := range cs { - if err := r.Register(c); err != nil { - panic(err) - } - } + Deliver() ([]*dto.MetricFamily, error) } // Register registers the provided Collector with the DefaultRegistry. @@ -153,7 +139,7 @@ func Register(c Collector) error { // MustRegister is a shortcut for MustRegisterWith(DefaultRegistry, cs...). See // there for more details. func MustRegister(cs ...Collector) { - MustRegisterWith(DefaultRegistry, cs...) + DefaultRegistry.MustRegister(cs...) } // RegisterOrGet registers the provided Collector with the DefaultRegistry and @@ -208,13 +194,13 @@ func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) { DefaultRegistry.SetInjectionHook(hook) } -// AlreadyRegisteredError is returned by the Registry.Register if the Collector -// to be registered has already been registered before, or a different Collector -// that collects the same metrics has been registered before. Registration fails -// in that case, but you can detect from the kind of error what has -// happened. The error contains fields for the existing Collector and the -// (rejected) new Collector that equals the existing one. This can be used in -// the following way: +// AlreadyRegisteredError is returned by the Registerer.Register if the +// Collector to be registered has already been registered before, or a different +// Collector that collects the same metrics has been registered +// before. Registration fails in that case, but you can detect from the kind of +// error what has happened. The error contains fields for the existing Collector +// and the (rejected) new Collector that equals the existing one. This can be +// used in the following way: // // reqCounter := prometheus.NewCounter( /* ... */ ) // if err := registry.Register(reqCounter); err != nil { @@ -235,7 +221,11 @@ func (err AlreadyRegisteredError) Error() string { return "duplicate metrics collector registration attempted" } -type registry struct { +// Registry registers Prometheus collectors, collects their metrics, and +// delivers them for exposition. It implements Registerer and Deliverer. The +// zero value is not usable. Use NewRegistry or NewPedanticRegistry to create +// instances. +type Registry struct { mtx sync.RWMutex collectorsByID map[uint64]Collector // ID is a hash of the descIDs. descIDs map[uint64]struct{} @@ -244,7 +234,8 @@ type registry struct { pedanticChecksEnabled bool } -func (r *registry) Register(c Collector) error { +// Register implements Registerer. +func (r *Registry) Register(c Collector) error { var ( descChan = make(chan *Desc, capDescChan) newDescIDs = map[uint64]struct{}{} @@ -324,7 +315,8 @@ func (r *registry) Register(c Collector) error { return nil } -func (r *registry) Unregister(c Collector) bool { +// Unregister implements Registerer. +func (r *Registry) Unregister(c Collector) bool { var ( descChan = make(chan *Desc, capDescChan) descIDs = map[uint64]struct{}{} @@ -360,7 +352,17 @@ func (r *registry) Unregister(c Collector) bool { return true } -func (r *registry) Collect() ([]*dto.MetricFamily, error) { +// MustRegister implements Registerer. +func (r *Registry) MustRegister(cs ...Collector) { + for _, c := range cs { + if err := r.Register(c); err != nil { + panic(err) + } + } +} + +// Deliver implements Deliverer. +func (r *Registry) Deliver() ([]*dto.MetricFamily, error) { var ( metricChan = make(chan Metric, capMetricChan) metricHashes = map[uint64]struct{}{} @@ -495,7 +497,7 @@ func (r *registry) Collect() ([]*dto.MetricFamily, error) { return result, errs } -func (r *registry) checkConsistency( +func (r *Registry) checkConsistency( metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, desc *Desc, @@ -583,7 +585,20 @@ func (r *registry) checkConsistency( return nil } -func (r *registry) SetInjectionHook(hook func() []*dto.MetricFamily) { +// SetInjectionHook sets the provided hook to inject MetricFamilies. The hook is +// a function that is called whenever metrics are collected. The MetricFamily +// protobufs returned by the hook function are merged with the metrics collected +// in the usual way. +// +// This is a way to directly inject MetricFamily protobufs managed and owned by +// the caller. The caller has full responsibility. As no registration of the +// injected metrics has happened, there was no check at registration time. If +// the injection results in inconsistent metrics, the Collect call will return +// an error. Some problems may even go undetected, like invalid label names in +// the injected protobufs. +// +// The hook function must be callable at any time and concurrently. +func (r *Registry) SetInjectionHook(hook func() []*dto.MetricFamily) { r.mtx.Lock() defer r.mtx.Unlock() r.metricFamilyInjectionHook = hook