github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/logentry/metric/metricvec.go (about) 1 package metric 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/grafana/loki/pkg/util" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/common/model" 11 ) 12 13 // Expirable allows checking if something has exceeded the provided maxAge based on the provided currentTime 14 type Expirable interface { 15 HasExpired(currentTimeSec int64, maxAgeSec int64) bool 16 } 17 18 type metricVec struct { 19 factory func(labels map[string]string) prometheus.Metric 20 mtx sync.Mutex 21 metrics map[model.Fingerprint]prometheus.Metric 22 maxAgeSec int64 23 } 24 25 func newMetricVec(factory func(labels map[string]string) prometheus.Metric, maxAgeSec int64) *metricVec { 26 return &metricVec{ 27 metrics: map[model.Fingerprint]prometheus.Metric{}, 28 factory: factory, 29 maxAgeSec: maxAgeSec, 30 } 31 } 32 33 // Describe implements prometheus.Collector and doesn't declare any metrics on purpose to bypass prometheus validation. 34 // see https://godoc.org/github.com/prometheus/client_golang/prometheus#hdr-Custom_Collectors_and_constant_Metrics search for "unchecked" 35 func (c *metricVec) Describe(ch chan<- *prometheus.Desc) {} 36 37 // Collect implements prometheus.Collector 38 func (c *metricVec) Collect(ch chan<- prometheus.Metric) { 39 c.mtx.Lock() 40 defer c.mtx.Unlock() 41 for _, m := range c.metrics { 42 ch <- m 43 } 44 c.prune() 45 } 46 47 // With returns the metric associated with the labelset. 48 func (c *metricVec) With(labels model.LabelSet) prometheus.Metric { 49 c.mtx.Lock() 50 defer c.mtx.Unlock() 51 fp := labels.Fingerprint() 52 var ok bool 53 var metric prometheus.Metric 54 if metric, ok = c.metrics[fp]; !ok { 55 metric = c.factory(util.ModelLabelSetToMap(labels)) 56 c.metrics[fp] = metric 57 } 58 return metric 59 } 60 61 func (c *metricVec) Delete(labels model.LabelSet) bool { 62 c.mtx.Lock() 63 defer c.mtx.Unlock() 64 fp := labels.Fingerprint() 65 _, ok := c.metrics[fp] 66 if ok { 67 delete(c.metrics, fp) 68 } 69 return ok 70 } 71 72 // prune will remove all metrics which implement the Expirable interface and have expired 73 // it does not take out a lock on the metrics map so whoever calls this function should do so. 74 func (c *metricVec) prune() { 75 currentTimeSec := time.Now().Unix() 76 for fp, m := range c.metrics { 77 if em, ok := m.(Expirable); ok { 78 if em.HasExpired(currentTimeSec, c.maxAgeSec) { 79 delete(c.metrics, fp) 80 } 81 } 82 } 83 }