104 lines
2.9 KiB
Go
104 lines
2.9 KiB
Go
package metrics
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// per `prometheus/common/model/metric.go` and the `IsValidMetricName` function
|
|
// only alphanumeric characters, underscore and `:` are valid characters in
|
|
// a Prometheus metric name
|
|
var invalidPromChars = regexp.MustCompile(`[^[:alnum:]\_:]+`)
|
|
|
|
// promAdjust adjusts a name for use by Prometheus: It and replaces "-" and "."
|
|
// with "_". Invalid metric name characters that remain (e.g. `>`) are removed.
|
|
func promAdjust(name string) string {
|
|
name = strings.Replace(name, "-", "_", -1)
|
|
name = strings.Replace(name, ".", "_", -1)
|
|
return invalidPromChars.ReplaceAllString(name, "")
|
|
}
|
|
|
|
// autoProm implements a bridge from statsd-style metrics to Prometheus-style
|
|
// metrics, automatically registering metrics the first time they are used and
|
|
// memoizing them (since Prometheus doesn't allow repeat registration of the
|
|
// same metric). It is safe for concurrent access.
|
|
type autoProm struct {
|
|
sync.RWMutex
|
|
prometheus.Registerer
|
|
metrics map[string]prometheus.Collector
|
|
}
|
|
|
|
type maker func(string) prometheus.Collector
|
|
|
|
func (ap *autoProm) get(name string, make maker) prometheus.Collector {
|
|
name = promAdjust(name)
|
|
ap.RLock()
|
|
result := ap.metrics[name]
|
|
ap.RUnlock()
|
|
if result != nil {
|
|
return result
|
|
}
|
|
ap.Lock()
|
|
defer ap.Unlock()
|
|
|
|
// Check once more, since it could have been added while we were locked.
|
|
if ap.metrics[name] != nil {
|
|
return ap.metrics[name]
|
|
}
|
|
result = make(name)
|
|
ap.Registerer.MustRegister(result)
|
|
ap.metrics[name] = result
|
|
return result
|
|
}
|
|
|
|
func newAutoProm(registerer prometheus.Registerer) *autoProm {
|
|
return &autoProm{
|
|
metrics: make(map[string]prometheus.Collector),
|
|
Registerer: registerer,
|
|
}
|
|
}
|
|
|
|
// autoRegisterer wraps three autoProm instances, one for each type of metric
|
|
// managed by this module, and provides methods to get/make those metrics.
|
|
type autoRegisterer struct {
|
|
gauges, counters, summaries *autoProm
|
|
}
|
|
|
|
func newAutoRegisterer(registerer prometheus.Registerer) *autoRegisterer {
|
|
return &autoRegisterer{
|
|
gauges: newAutoProm(registerer),
|
|
counters: newAutoProm(registerer),
|
|
summaries: newAutoProm(registerer),
|
|
}
|
|
}
|
|
|
|
func (ar *autoRegisterer) autoGauge(name string) prometheus.Gauge {
|
|
return ar.gauges.get(name, func(cleaned string) prometheus.Collector {
|
|
return prometheus.NewGauge(prometheus.GaugeOpts{
|
|
Name: cleaned,
|
|
Help: "auto",
|
|
})
|
|
}).(prometheus.Gauge)
|
|
}
|
|
|
|
func (ar *autoRegisterer) autoCounter(name string) prometheus.Counter {
|
|
return ar.counters.get(name, func(cleaned string) prometheus.Collector {
|
|
return prometheus.NewCounter(prometheus.CounterOpts{
|
|
Name: cleaned,
|
|
Help: "auto",
|
|
})
|
|
}).(prometheus.Counter)
|
|
}
|
|
|
|
func (ar *autoRegisterer) autoSummary(name string) prometheus.Summary {
|
|
return ar.summaries.get(name, func(cleaned string) prometheus.Collector {
|
|
return prometheus.NewSummary(prometheus.SummaryOpts{
|
|
Name: cleaned,
|
|
Help: "auto",
|
|
})
|
|
}).(prometheus.Summary)
|
|
}
|