forked from mirror/client_golang
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
|
||||
// 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.
|
||||
|
|
|
@ -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