153 lines
3.5 KiB
Go
153 lines
3.5 KiB
Go
package metric
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform/errors"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
// REDClient is a metrics client for collection RED metrics.
|
|
type REDClient struct {
|
|
metrics []metricCollector
|
|
}
|
|
|
|
// New creates a new REDClient.
|
|
func New(reg prometheus.Registerer, service string, opts ...ClientOptFn) *REDClient {
|
|
opt := metricOpts{
|
|
namespace: "service",
|
|
service: service,
|
|
counterMetrics: map[string]VecOpts{
|
|
"call_total": {
|
|
Help: "Number of calls",
|
|
LabelNames: []string{"method"},
|
|
CounterFn: func(vec *prometheus.CounterVec, o CollectFnOpts) {
|
|
vec.With(prometheus.Labels{"method": o.Method}).Inc()
|
|
},
|
|
},
|
|
"error_total": {
|
|
Help: "Number of errors encountered",
|
|
LabelNames: []string{"method", "code"},
|
|
CounterFn: func(vec *prometheus.CounterVec, o CollectFnOpts) {
|
|
if o.Err != nil {
|
|
vec.With(prometheus.Labels{
|
|
"method": o.Method,
|
|
"code": errors.ErrorCode(o.Err),
|
|
}).Inc()
|
|
}
|
|
},
|
|
},
|
|
},
|
|
histogramMetrics: map[string]VecOpts{
|
|
"duration": {
|
|
Help: "Duration of calls",
|
|
LabelNames: []string{"method"},
|
|
HistogramFn: func(vec *prometheus.HistogramVec, o CollectFnOpts) {
|
|
vec.
|
|
With(prometheus.Labels{"method": o.Method}).
|
|
Observe(time.Since(o.Start).Seconds())
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, o := range opts {
|
|
o(&opt)
|
|
}
|
|
|
|
client := new(REDClient)
|
|
for metricName, vecOpts := range opt.counterMetrics {
|
|
client.metrics = append(client.metrics, &counter{
|
|
fn: vecOpts.CounterFn,
|
|
CounterVec: prometheus.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: opt.namespace,
|
|
Subsystem: opt.serviceName(),
|
|
Name: metricName,
|
|
Help: vecOpts.Help,
|
|
}, vecOpts.LabelNames),
|
|
})
|
|
}
|
|
|
|
for metricName, vecOpts := range opt.histogramMetrics {
|
|
client.metrics = append(client.metrics, &histogram{
|
|
fn: vecOpts.HistogramFn,
|
|
HistogramVec: prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
Namespace: opt.namespace,
|
|
Subsystem: opt.serviceName(),
|
|
Name: metricName,
|
|
Help: vecOpts.Help,
|
|
}, vecOpts.LabelNames),
|
|
})
|
|
}
|
|
|
|
reg.MustRegister(client.collectors()...)
|
|
|
|
return client
|
|
}
|
|
|
|
type RecordFn func(err error, opts ...func(opts *CollectFnOpts)) error
|
|
|
|
// RecordAdditional provides an extension to the base method, err data provided
|
|
// to the metrics.
|
|
func RecordAdditional(props map[string]interface{}) func(opts *CollectFnOpts) {
|
|
return func(opts *CollectFnOpts) {
|
|
opts.AdditionalProps = props
|
|
}
|
|
}
|
|
|
|
// Record returns a record fn that is called on any given return err. If an error is encountered
|
|
// it will register the err metric. The err is never altered.
|
|
func (c *REDClient) Record(method string) RecordFn {
|
|
start := time.Now()
|
|
return func(err error, opts ...func(opts *CollectFnOpts)) error {
|
|
opt := CollectFnOpts{
|
|
Method: method,
|
|
Start: start,
|
|
Err: err,
|
|
}
|
|
for _, o := range opts {
|
|
o(&opt)
|
|
}
|
|
|
|
for _, metric := range c.metrics {
|
|
metric.collect(opt)
|
|
}
|
|
|
|
return err
|
|
}
|
|
}
|
|
|
|
func (c *REDClient) collectors() []prometheus.Collector {
|
|
var collectors []prometheus.Collector
|
|
for _, metric := range c.metrics {
|
|
collectors = append(collectors, metric)
|
|
}
|
|
return collectors
|
|
}
|
|
|
|
type metricCollector interface {
|
|
prometheus.Collector
|
|
|
|
collect(o CollectFnOpts)
|
|
}
|
|
|
|
type counter struct {
|
|
*prometheus.CounterVec
|
|
|
|
fn CounterFn
|
|
}
|
|
|
|
func (c *counter) collect(o CollectFnOpts) {
|
|
c.fn(c.CounterVec, o)
|
|
}
|
|
|
|
type histogram struct {
|
|
*prometheus.HistogramVec
|
|
|
|
fn HistogramFn
|
|
}
|
|
|
|
func (h *histogram) collect(o CollectFnOpts) {
|
|
h.fn(h.HistogramVec, o)
|
|
}
|