83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
package metric
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/lixh00/loki-client-go/pkg/labelutil"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/common/model"
|
|
)
|
|
|
|
// Expirable allows checking if something has exceeded the provided maxAge based on the provided currentTime
|
|
type Expirable interface {
|
|
HasExpired(currentTimeSec int64, maxAgeSec int64) bool
|
|
}
|
|
|
|
type metricVec struct {
|
|
factory func(labels map[string]string) prometheus.Metric
|
|
mtx sync.Mutex
|
|
metrics map[model.Fingerprint]prometheus.Metric
|
|
maxAgeSec int64
|
|
}
|
|
|
|
func newMetricVec(factory func(labels map[string]string) prometheus.Metric, maxAgeSec int64) *metricVec {
|
|
return &metricVec{
|
|
metrics: map[model.Fingerprint]prometheus.Metric{},
|
|
factory: factory,
|
|
maxAgeSec: maxAgeSec,
|
|
}
|
|
}
|
|
|
|
// Describe implements prometheus.Collector and doesn't declare any metrics on purpose to bypass prometheus validation.
|
|
// see https://godoc.org/github.com/prometheus/client_golang/prometheus#hdr-Custom_Collectors_and_constant_Metrics search for "unchecked"
|
|
func (c *metricVec) Describe(ch chan<- *prometheus.Desc) {}
|
|
|
|
// Collect implements prometheus.Collector
|
|
func (c *metricVec) Collect(ch chan<- prometheus.Metric) {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
for _, m := range c.metrics {
|
|
ch <- m
|
|
}
|
|
c.prune()
|
|
}
|
|
|
|
// With returns the metric associated with the labelset.
|
|
func (c *metricVec) With(labels model.LabelSet) prometheus.Metric {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
fp := labels.Fingerprint()
|
|
var ok bool
|
|
var metric prometheus.Metric
|
|
if metric, ok = c.metrics[fp]; !ok {
|
|
metric = c.factory(labelutil.ModelLabelSetToMap(labels))
|
|
c.metrics[fp] = metric
|
|
}
|
|
return metric
|
|
}
|
|
|
|
func (c *metricVec) Delete(labels model.LabelSet) bool {
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
fp := labels.Fingerprint()
|
|
_, ok := c.metrics[fp]
|
|
if ok {
|
|
delete(c.metrics, fp)
|
|
}
|
|
return ok
|
|
}
|
|
|
|
// prune will remove all metrics which implement the Expirable interface and have expired
|
|
// it does not take out a lock on the metrics map so whoever calls this function should do so.
|
|
func (c *metricVec) prune() {
|
|
currentTimeSec := time.Now().Unix()
|
|
for fp, m := range c.metrics {
|
|
if em, ok := m.(Expirable); ok {
|
|
if em.HasExpired(currentTimeSec, c.maxAgeSec) {
|
|
delete(c.metrics, fp)
|
|
}
|
|
}
|
|
}
|
|
}
|