diff --git a/prometheus/registry.go b/prometheus/registry.go index 325f665ff..09e34d307 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -252,9 +252,12 @@ func (errs MultiError) MaybeUnwrap() error { } // Registry registers Prometheus collectors, collects their metrics, and gathers -// them into MetricFamilies for exposition. It implements both Registerer and -// Gatherer. The zero value is not usable. Create instances with NewRegistry or -// NewPedanticRegistry. +// them into MetricFamilies for exposition. It implements Registerer, Gatherer, +// and Collector. The zero value is not usable. Create instances with +// NewRegistry or NewPedanticRegistry. +// +// Registry implements Collector to allow it to be used for creating groups of +// metrics. See the Grouping example for how this can be done. type Registry struct { mtx sync.RWMutex collectorsByID map[uint64]Collector // ID is a hash of the descIDs. @@ -556,6 +559,31 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() } +// Describe implements Collector. +func (r *Registry) Describe(ch chan<- *Desc) { + r.mtx.RLock() + defer r.mtx.RUnlock() + + // Only report the checked Collectors; unchecked collectors don't report any + // Desc. + for _, c := range r.collectorsByID { + c.Describe(ch) + } +} + +// Collect implements Collector. +func (r *Registry) Collect(ch chan<- Metric) { + r.mtx.RLock() + defer r.mtx.RUnlock() + + for _, c := range r.collectorsByID { + c.Collect(ch) + } + for _, c := range r.uncheckedCollectors { + c.Collect(ch) + } +} + // WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the // Prometheus text format, and writes it to a temporary file. Upon success, the // temporary file is renamed to the provided filename. diff --git a/prometheus/registry_test.go b/prometheus/registry_test.go index f0871ba87..3faf67aa5 100644 --- a/prometheus/registry_test.go +++ b/prometheus/registry_test.go @@ -1254,3 +1254,37 @@ func TestNewMultiTRegistry(t *testing.T) { } }) } + +// This example shows how to use multiple registries for registering and +// unregistering groups of metrics. +func ExampleRegistry_grouping() { + // Create a global registry. + globalReg := prometheus.NewRegistry() + + // Spawn 10 workers, each of which will have their own group of metrics. + for i := 0; i < 10; i++ { + // Create a new registry for each worker, which acts as a group of + // worker-specific metrics. + workerReg := prometheus.NewRegistry() + globalReg.Register(workerReg) + + go func(workerID int) { + // Once the worker is done, it can unregister itself. + defer globalReg.Unregister(workerReg) + + workTime := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "worker_total_work_time_milliseconds", + ConstLabels: prometheus.Labels{ + // Generate a label unique to this worker so its metric doesn't + // collide with the metrics from other workers. + "worker_id": fmt.Sprintf("%d", workerID), + }, + }) + workerReg.MustRegister(workTime) + + start := time.Now() + time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) + workTime.Add(float64(time.Since(start).Milliseconds())) + }(i) + } +}