Path: blob/main/component/loki/process/internal/metric/metricvec.go
4096 views
package metric12import (3"strings"4"sync"5"time"67"github.com/grafana/loki/pkg/util"89"github.com/prometheus/client_golang/prometheus"10"github.com/prometheus/common/model"11)1213// Expirable allows checking if something has exceeded the provided maxAge based on the provided currentTime14type Expirable interface {15HasExpired(currentTimeSec int64, maxAgeSec int64) bool16}1718type metricVec struct {19factory func(labels map[string]string) prometheus.Metric20mtx sync.Mutex21metrics map[model.Fingerprint]prometheus.Metric22maxAgeSec int6423}2425func newMetricVec(factory func(labels map[string]string) prometheus.Metric, maxAgeSec int64) *metricVec {26return &metricVec{27metrics: map[model.Fingerprint]prometheus.Metric{},28factory: factory,29maxAgeSec: maxAgeSec,30}31}3233// 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"35func (c *metricVec) Describe(ch chan<- *prometheus.Desc) {}3637// Collect implements prometheus.Collector38func (c *metricVec) Collect(ch chan<- prometheus.Metric) {39c.mtx.Lock()40defer c.mtx.Unlock()41for _, m := range c.metrics {42ch <- m43}44c.prune()45}4647// With returns the metric associated with the labelset.48func (c *metricVec) With(labels model.LabelSet) prometheus.Metric {49c.mtx.Lock()50defer c.mtx.Unlock()51fp := labels.Fingerprint()52var ok bool53var metric prometheus.Metric54if metric, ok = c.metrics[fp]; !ok {55metric = c.factory(util.ModelLabelSetToMap(cleanLabels(labels)))56c.metrics[fp] = metric57}58return metric59}6061// cleanLabels removes labels whose label name is not a valid prometheus one, or has the reserved `__` prefix.62func cleanLabels(set model.LabelSet) model.LabelSet {63out := make(model.LabelSet, len(set))64for k, v := range set {65// Performing the same label validity check the prometheus go client library does.66// https://github.com/prometheus/client_golang/blob/618194de6ad3db637313666104533639011b470d/prometheus/labels.go#L8567if !k.IsValid() || strings.HasPrefix(string(k), "__") {68continue69}70out[k] = v71}72return out73}7475func (c *metricVec) Delete(labels model.LabelSet) bool {76c.mtx.Lock()77defer c.mtx.Unlock()78fp := labels.Fingerprint()79_, ok := c.metrics[fp]80if ok {81delete(c.metrics, fp)82}83return ok84}8586// prune will remove all metrics which implement the Expirable interface and have expired87// it does not take out a lock on the metrics map so whoever calls this function should do so.88func (c *metricVec) prune() {89currentTimeSec := time.Now().Unix()90for fp, m := range c.metrics {91if em, ok := m.(Expirable); ok {92if em.HasExpired(currentTimeSec, c.maxAgeSec) {93delete(c.metrics, fp)94}95}96}97}9899100