package prometheus
import (
"fmt"
"github.com/google/trillian/monitoring"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
"k8s.io/klog/v2"
)
type MetricFactory struct {
Prefix string
}
func (pmf MetricFactory) NewCounter(name, help string, labelNames ...string) monitoring.Counter {
if len(labelNames) == 0 {
counter := prometheus.NewCounter(
prometheus.CounterOpts{
Name: pmf.Prefix + name,
Help: help,
})
prometheus.MustRegister(counter)
return &Counter{single: counter}
}
vec := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: pmf.Prefix + name,
Help: help,
},
labelNames)
prometheus.MustRegister(vec)
return &Counter{labelNames: labelNames, vec: vec}
}
func (pmf MetricFactory) NewGauge(name, help string, labelNames ...string) monitoring.Gauge {
if len(labelNames) == 0 {
gauge := prometheus.NewGauge(
prometheus.GaugeOpts{
Name: pmf.Prefix + name,
Help: help,
})
prometheus.MustRegister(gauge)
return &Gauge{single: gauge}
}
vec := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: pmf.Prefix + name,
Help: help,
},
labelNames)
prometheus.MustRegister(vec)
return &Gauge{labelNames: labelNames, vec: vec}
}
func (pmf MetricFactory) NewHistogramWithBuckets(name, help string, buckets []float64, labelNames ...string) monitoring.Histogram {
return pmf.newHistogram(name, help, buckets, labelNames)
}
func (pmf MetricFactory) NewHistogram(name, help string, labelNames ...string) monitoring.Histogram {
return pmf.newHistogram(name, help, monitoring.LatencyBuckets(), labelNames)
}
func (pmf MetricFactory) newHistogram(name, help string, buckets []float64, labelNames []string) monitoring.Histogram {
if len(labelNames) == 0 {
histogram := prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: pmf.Prefix + name,
Help: help,
Buckets: buckets,
})
prometheus.MustRegister(histogram)
return &Histogram{single: histogram}
}
vec := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: pmf.Prefix + name,
Help: help,
Buckets: buckets,
},
labelNames)
prometheus.MustRegister(vec)
return &Histogram{labelNames: labelNames, vec: vec}
}
type Counter struct {
labelNames []string
single prometheus.Counter
vec *prometheus.CounterVec
}
func (m *Counter) Inc(labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Inc()
} else {
m.single.Inc()
}
}
func (m *Counter) Add(val float64, labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Add(val)
} else {
m.single.Add(val)
}
}
func (m *Counter) Value(labelVals ...string) float64 {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return 0.0
}
var metric prometheus.Metric
if m.vec != nil {
metric = m.vec.With(labels)
} else {
metric = m.single
}
var metricpb dto.Metric
if err := metric.Write(&metricpb); err != nil {
klog.Errorf("failed to Write metric: %v", err)
return 0.0
}
if metricpb.Counter == nil {
klog.Errorf("counter field missing")
return 0.0
}
return metricpb.Counter.GetValue()
}
type Gauge struct {
labelNames []string
single prometheus.Gauge
vec *prometheus.GaugeVec
}
func (m *Gauge) Inc(labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Inc()
} else {
m.single.Inc()
}
}
func (m *Gauge) Dec(labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Dec()
} else {
m.single.Dec()
}
}
func (m *Gauge) Add(val float64, labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Add(val)
} else {
m.single.Add(val)
}
}
func (m *Gauge) Set(val float64, labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Set(val)
} else {
m.single.Set(val)
}
}
func (m *Gauge) Value(labelVals ...string) float64 {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return 0.0
}
var metric prometheus.Metric
if m.vec != nil {
metric = m.vec.With(labels)
} else {
metric = m.single
}
var metricpb dto.Metric
if err := metric.Write(&metricpb); err != nil {
klog.Errorf("failed to Write metric: %v", err)
return 0.0
}
if metricpb.Gauge == nil {
klog.Errorf("gauge field missing")
return 0.0
}
return metricpb.Gauge.GetValue()
}
type Histogram struct {
labelNames []string
single prometheus.Histogram
vec *prometheus.HistogramVec
}
func (m *Histogram) Observe(val float64, labelVals ...string) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return
}
if m.vec != nil {
m.vec.With(labels).Observe(val)
} else {
m.single.Observe(val)
}
}
func (m *Histogram) Info(labelVals ...string) (uint64, float64) {
labels, err := labelsFor(m.labelNames, labelVals)
if err != nil {
klog.Error(err.Error())
return 0, 0.0
}
var metric prometheus.Metric
if m.vec != nil {
metric = m.vec.With(labels).(prometheus.Metric)
} else {
metric = m.single
}
var metricpb dto.Metric
if err := metric.Write(&metricpb); err != nil {
klog.Errorf("failed to Write metric: %v", err)
return 0, 0.0
}
histVal := metricpb.GetHistogram()
if histVal == nil {
klog.Errorf("histogram field missing")
return 0, 0.0
}
return histVal.GetSampleCount(), histVal.GetSampleSum()
}
func labelsFor(names, values []string) (prometheus.Labels, error) {
if len(names) != len(values) {
return nil, fmt.Errorf("got %d (%v) values for %d labels (%v)", len(values), values, len(names), names)
}
if len(names) == 0 {
return nil, nil
}
labels := make(prometheus.Labels)
for i, name := range names {
labels[name] = values[i]
}
return labels, nil
}