Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/component/loki/process/internal/metric/metricvec.go
4096 views
1
package metric
2
3
import (
4
"strings"
5
"sync"
6
"time"
7
8
"github.com/grafana/loki/pkg/util"
9
10
"github.com/prometheus/client_golang/prometheus"
11
"github.com/prometheus/common/model"
12
)
13
14
// Expirable allows checking if something has exceeded the provided maxAge based on the provided currentTime
15
type Expirable interface {
16
HasExpired(currentTimeSec int64, maxAgeSec int64) bool
17
}
18
19
type metricVec struct {
20
factory func(labels map[string]string) prometheus.Metric
21
mtx sync.Mutex
22
metrics map[model.Fingerprint]prometheus.Metric
23
maxAgeSec int64
24
}
25
26
func newMetricVec(factory func(labels map[string]string) prometheus.Metric, maxAgeSec int64) *metricVec {
27
return &metricVec{
28
metrics: map[model.Fingerprint]prometheus.Metric{},
29
factory: factory,
30
maxAgeSec: maxAgeSec,
31
}
32
}
33
34
// Describe implements prometheus.Collector and doesn't declare any metrics on purpose to bypass prometheus validation.
35
// see https://godoc.org/github.com/prometheus/client_golang/prometheus#hdr-Custom_Collectors_and_constant_Metrics search for "unchecked"
36
func (c *metricVec) Describe(ch chan<- *prometheus.Desc) {}
37
38
// Collect implements prometheus.Collector
39
func (c *metricVec) Collect(ch chan<- prometheus.Metric) {
40
c.mtx.Lock()
41
defer c.mtx.Unlock()
42
for _, m := range c.metrics {
43
ch <- m
44
}
45
c.prune()
46
}
47
48
// With returns the metric associated with the labelset.
49
func (c *metricVec) With(labels model.LabelSet) prometheus.Metric {
50
c.mtx.Lock()
51
defer c.mtx.Unlock()
52
fp := labels.Fingerprint()
53
var ok bool
54
var metric prometheus.Metric
55
if metric, ok = c.metrics[fp]; !ok {
56
metric = c.factory(util.ModelLabelSetToMap(cleanLabels(labels)))
57
c.metrics[fp] = metric
58
}
59
return metric
60
}
61
62
// cleanLabels removes labels whose label name is not a valid prometheus one, or has the reserved `__` prefix.
63
func cleanLabels(set model.LabelSet) model.LabelSet {
64
out := make(model.LabelSet, len(set))
65
for k, v := range set {
66
// Performing the same label validity check the prometheus go client library does.
67
// https://github.com/prometheus/client_golang/blob/618194de6ad3db637313666104533639011b470d/prometheus/labels.go#L85
68
if !k.IsValid() || strings.HasPrefix(string(k), "__") {
69
continue
70
}
71
out[k] = v
72
}
73
return out
74
}
75
76
func (c *metricVec) Delete(labels model.LabelSet) bool {
77
c.mtx.Lock()
78
defer c.mtx.Unlock()
79
fp := labels.Fingerprint()
80
_, ok := c.metrics[fp]
81
if ok {
82
delete(c.metrics, fp)
83
}
84
return ok
85
}
86
87
// prune will remove all metrics which implement the Expirable interface and have expired
88
// it does not take out a lock on the metrics map so whoever calls this function should do so.
89
func (c *metricVec) prune() {
90
currentTimeSec := time.Now().Unix()
91
for fp, m := range c.metrics {
92
if em, ok := m.(Expirable); ok {
93
if em.HasExpired(currentTimeSec, c.maxAgeSec) {
94
delete(c.metrics, fp)
95
}
96
}
97
}
98
}
99
100