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  }