diff options
author | hcpp <hcpp@ydb.tech> | 2023-11-08 12:09:41 +0300 |
---|---|---|
committer | hcpp <hcpp@ydb.tech> | 2023-11-08 12:56:14 +0300 |
commit | a361f5b98b98b44ea510d274f6769164640dd5e1 (patch) | |
tree | c47c80962c6e2e7b06798238752fd3da0191a3f6 /library/go/core/metrics/prometheus/registry.go | |
parent | 9478806fde1f4d40bd5a45e7cbe77237dab613e9 (diff) | |
download | ydb-a361f5b98b98b44ea510d274f6769164640dd5e1.tar.gz |
metrics have been added
Diffstat (limited to 'library/go/core/metrics/prometheus/registry.go')
-rw-r--r-- | library/go/core/metrics/prometheus/registry.go | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/library/go/core/metrics/prometheus/registry.go b/library/go/core/metrics/prometheus/registry.go new file mode 100644 index 0000000000..bad45fe617 --- /dev/null +++ b/library/go/core/metrics/prometheus/registry.go @@ -0,0 +1,254 @@ +package prometheus + +import ( + "sync" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/ydb-platform/ydb/library/go/core/metrics" + "github.com/ydb-platform/ydb/library/go/core/metrics/internal/pkg/metricsutil" + "github.com/ydb-platform/ydb/library/go/core/metrics/internal/pkg/registryutil" + "github.com/ydb-platform/ydb/library/go/core/xerrors" +) + +var _ metrics.Registry = (*Registry)(nil) + +type Registry struct { + rg *prometheus.Registry + + m *sync.Mutex + subregistries map[string]*Registry + + tags map[string]string + prefix string + nameSanitizer func(string) string +} + +// NewRegistry creates new Prometheus backed registry. +func NewRegistry(opts *RegistryOpts) *Registry { + r := &Registry{ + rg: prometheus.NewRegistry(), + m: new(sync.Mutex), + subregistries: make(map[string]*Registry), + tags: make(map[string]string), + } + + if opts != nil { + r.prefix = opts.Prefix + r.tags = opts.Tags + if opts.rg != nil { + r.rg = opts.rg + } + for _, collector := range opts.Collectors { + collector(r) + } + if opts.NameSanitizer != nil { + r.nameSanitizer = opts.NameSanitizer + } + } + + return r +} + +// WithTags creates new sub-scope, where each metric has tags attached to it. +func (r Registry) WithTags(tags map[string]string) metrics.Registry { + return r.newSubregistry(r.prefix, registryutil.MergeTags(r.tags, tags)) +} + +// WithPrefix creates new sub-scope, where each metric has prefix added to it name. +func (r Registry) WithPrefix(prefix string) metrics.Registry { + return r.newSubregistry(registryutil.BuildFQName("_", r.prefix, prefix), r.tags) +} + +// ComposeName builds FQ name with appropriate separator. +func (r Registry) ComposeName(parts ...string) string { + return registryutil.BuildFQName("_", parts...) +} + +func (r Registry) Counter(name string) metrics.Counter { + name = r.sanitizeName(name) + cnt := prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }) + + if err := r.rg.Register(cnt); err != nil { + var existErr prometheus.AlreadyRegisteredError + if xerrors.As(err, &existErr) { + return &Counter{cnt: existErr.ExistingCollector.(prometheus.Counter)} + } + panic(err) + } + + return &Counter{cnt: cnt} +} + +func (r Registry) FuncCounter(name string, function func() int64) metrics.FuncCounter { + name = r.sanitizeName(name) + cnt := prometheus.NewCounterFunc(prometheus.CounterOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }, func() float64 { + return float64(function()) + }) + + if err := r.rg.Register(cnt); err != nil { + panic(err) + } + + return &FuncCounter{ + cnt: cnt, + function: function, + } +} + +func (r Registry) Gauge(name string) metrics.Gauge { + name = r.sanitizeName(name) + gg := prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }) + + if err := r.rg.Register(gg); err != nil { + var existErr prometheus.AlreadyRegisteredError + if xerrors.As(err, &existErr) { + return &Gauge{gg: existErr.ExistingCollector.(prometheus.Gauge)} + } + panic(err) + } + + return &Gauge{gg: gg} +} + +func (r Registry) FuncGauge(name string, function func() float64) metrics.FuncGauge { + name = r.sanitizeName(name) + ff := prometheus.NewGaugeFunc(prometheus.GaugeOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }, function) + if err := r.rg.Register(ff); err != nil { + panic(err) + } + return &FuncGauge{ + ff: ff, + function: function, + } +} + +func (r Registry) IntGauge(name string) metrics.IntGauge { + return &IntGauge{Gauge: r.Gauge(name)} +} + +func (r Registry) FuncIntGauge(name string, function func() int64) metrics.FuncIntGauge { + name = r.sanitizeName(name) + ff := prometheus.NewGaugeFunc(prometheus.GaugeOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }, func() float64 { return float64(function()) }) + if err := r.rg.Register(ff); err != nil { + panic(err) + } + return &FuncIntGauge{ + ff: ff, + function: function, + } +} + +func (r Registry) Timer(name string) metrics.Timer { + name = r.sanitizeName(name) + gg := prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + }) + + if err := r.rg.Register(gg); err != nil { + var existErr prometheus.AlreadyRegisteredError + if xerrors.As(err, &existErr) { + return &Timer{gg: existErr.ExistingCollector.(prometheus.Gauge)} + } + panic(err) + } + + return &Timer{gg: gg} +} + +func (r Registry) Histogram(name string, buckets metrics.Buckets) metrics.Histogram { + name = r.sanitizeName(name) + hm := prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + Buckets: metricsutil.BucketsBounds(buckets), + }) + + if err := r.rg.Register(hm); err != nil { + var existErr prometheus.AlreadyRegisteredError + if xerrors.As(err, &existErr) { + return &Histogram{hm: existErr.ExistingCollector.(prometheus.Observer)} + } + panic(err) + } + + return &Histogram{hm: hm} +} + +func (r Registry) DurationHistogram(name string, buckets metrics.DurationBuckets) metrics.Timer { + name = r.sanitizeName(name) + hm := prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: r.prefix, + Name: name, + ConstLabels: r.tags, + Buckets: metricsutil.DurationBucketsBounds(buckets), + }) + + if err := r.rg.Register(hm); err != nil { + var existErr prometheus.AlreadyRegisteredError + if xerrors.As(err, &existErr) { + return &Histogram{hm: existErr.ExistingCollector.(prometheus.Histogram)} + } + panic(err) + } + + return &Histogram{hm: hm} +} + +// Gather returns raw collected Prometheus metrics. +func (r Registry) Gather() ([]*dto.MetricFamily, error) { + return r.rg.Gather() +} + +func (r *Registry) newSubregistry(prefix string, tags map[string]string) *Registry { + registryKey := registryutil.BuildRegistryKey(prefix, tags) + + r.m.Lock() + defer r.m.Unlock() + + if old, ok := r.subregistries[registryKey]; ok { + return old + } + + subregistry := &Registry{ + rg: r.rg, + m: r.m, + subregistries: r.subregistries, + tags: tags, + prefix: prefix, + nameSanitizer: r.nameSanitizer, + } + + r.subregistries[registryKey] = subregistry + return subregistry +} + +func (r *Registry) sanitizeName(name string) string { + if r.nameSanitizer == nil { + return name + } + return r.nameSanitizer(name) +} |