Extend prometheus.Registry to implement Collector (#1103)
* prometheus: implement Collector interface for Registry This change allows Registries to be used as Collectors. This enables new instances of Registry to be passed to ephemeral subroutines for collecting metrics from subroutines which are still running: ```go package main import ( "fmt" "github.com/prometheus/client_golang/prometheus" ) func main() { globalReg := prometheus.NewRegistry() for i := 0; i < 100; i++ { workerReg := prometheus.WrapRegistererWith(prometheus.Labels{ // Add an ID label so registered metrics from workers don't // collide. "worker_id": fmt.Sprintf("%d", i), }, prometheus.NewRegistry() globalReg.MustRegister(workerReg) go func(i int) { runWorker(workerReg) // Unregister any metrics the worker may have created. globalReg.Unregister(workerReg) }(i) } } // runWorker runs a worker, registering worker-specific metrics. func runWorker(reg *prometheus.Registry) { // ... register metrics ... // ... do work ... } ``` This change makes it easier to avoid leaking metrics from subroutines which do not consistently properly unregister metrics. Signed-off-by: Robert Fratto <robertfratto@gmail.com> * fix grammar in doc comment Signed-off-by: Robert Fratto <robertfratto@gmail.com> * document why Registry implements Collector with example Signed-off-by: Robert Fratto <robertfratto@gmail.com> Signed-off-by: Robert Fratto <robertfratto@gmail.com>
This commit is contained in:
parent
4c41dfbcd5
commit
83d56b1144
|
@ -252,9 +252,12 @@ func (errs MultiError) MaybeUnwrap() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry registers Prometheus collectors, collects their metrics, and gathers
|
// Registry registers Prometheus collectors, collects their metrics, and gathers
|
||||||
// them into MetricFamilies for exposition. It implements both Registerer and
|
// them into MetricFamilies for exposition. It implements Registerer, Gatherer,
|
||||||
// Gatherer. The zero value is not usable. Create instances with NewRegistry or
|
// and Collector. The zero value is not usable. Create instances with
|
||||||
// NewPedanticRegistry.
|
// 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 {
|
type Registry struct {
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
collectorsByID map[uint64]Collector // ID is a hash of the descIDs.
|
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()
|
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
|
// 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
|
// Prometheus text format, and writes it to a temporary file. Upon success, the
|
||||||
// temporary file is renamed to the provided filename.
|
// temporary file is renamed to the provided filename.
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue