github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/util/metrics_helper.go (about)

     1  package util
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"sync"
     9  
    10  	"github.com/go-kit/log/level"
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	dto "github.com/prometheus/client_model/go"
    13  	"github.com/prometheus/prometheus/model/labels"
    14  	tsdb_errors "github.com/prometheus/prometheus/tsdb/errors"
    15  
    16  	util_log "github.com/grafana/loki/pkg/util/log"
    17  )
    18  
    19  var (
    20  	bytesBufferPool = sync.Pool{
    21  		New: func() interface{} {
    22  			return bytes.NewBuffer(nil)
    23  		},
    24  	}
    25  )
    26  
    27  // Data for single value (counter/gauge) with labels.
    28  type singleValueWithLabels struct {
    29  	Value       float64
    30  	LabelValues []string
    31  }
    32  
    33  // Key to this map is value unique to label values (generated by getLabelsString function)
    34  type singleValueWithLabelsMap map[string]singleValueWithLabels
    35  
    36  // This function is used to aggregate results with different labels into a map. Values for same labels are added together.
    37  func (m singleValueWithLabelsMap) aggregateFn(labelsKey string, labelValues []string, value float64) {
    38  	r := m[labelsKey]
    39  	if r.LabelValues == nil {
    40  		r.LabelValues = labelValues
    41  	}
    42  
    43  	r.Value += value
    44  	m[labelsKey] = r
    45  }
    46  
    47  func (m singleValueWithLabelsMap) prependUserLabelValue(user string) {
    48  	for key, mlv := range m {
    49  		mlv.LabelValues = append([]string{user}, mlv.LabelValues...)
    50  		m[key] = mlv
    51  	}
    52  }
    53  
    54  func (m singleValueWithLabelsMap) WriteToMetricChannel(out chan<- prometheus.Metric, desc *prometheus.Desc, valueType prometheus.ValueType) {
    55  	for _, cr := range m {
    56  		out <- prometheus.MustNewConstMetric(desc, valueType, cr.Value, cr.LabelValues...)
    57  	}
    58  }
    59  
    60  // MetricFamilyMap is a map of metric names to their family (metrics with same name, but different labels)
    61  // Keeping map of metric name to its family makes it easier to do searches later.
    62  type MetricFamilyMap map[string]*dto.MetricFamily
    63  
    64  // NewMetricFamilyMap sorts output from Gatherer.Gather method into a map.
    65  // Gatherer.Gather specifies that there metric families are uniquely named, and we use that fact here.
    66  // If they are not, this method returns error.
    67  func NewMetricFamilyMap(metrics []*dto.MetricFamily) (MetricFamilyMap, error) {
    68  	perMetricName := MetricFamilyMap{}
    69  
    70  	for _, m := range metrics {
    71  		name := m.GetName()
    72  		// these errors should never happen when passing Gatherer.Gather() output.
    73  		if name == "" {
    74  			return nil, errors.New("empty name for metric family")
    75  		}
    76  		if perMetricName[name] != nil {
    77  			return nil, fmt.Errorf("non-unique name for metric family: %q", name)
    78  		}
    79  
    80  		perMetricName[name] = m
    81  	}
    82  
    83  	return perMetricName, nil
    84  }
    85  
    86  func (mfm MetricFamilyMap) SumCounters(name string) float64 {
    87  	return sum(mfm[name], counterValue)
    88  }
    89  
    90  func (mfm MetricFamilyMap) SumGauges(name string) float64 {
    91  	return sum(mfm[name], gaugeValue)
    92  }
    93  
    94  func (mfm MetricFamilyMap) MaxGauges(name string) float64 {
    95  	return max(mfm[name], gaugeValue)
    96  }
    97  
    98  func (mfm MetricFamilyMap) SumHistograms(name string) HistogramData {
    99  	hd := HistogramData{}
   100  	mfm.SumHistogramsTo(name, &hd)
   101  	return hd
   102  }
   103  
   104  func (mfm MetricFamilyMap) SumHistogramsTo(name string, output *HistogramData) {
   105  	for _, m := range mfm[name].GetMetric() {
   106  		output.AddHistogram(m.GetHistogram())
   107  	}
   108  }
   109  
   110  func (mfm MetricFamilyMap) SumSummaries(name string) SummaryData {
   111  	sd := SummaryData{}
   112  	mfm.SumSummariesTo(name, &sd)
   113  	return sd
   114  }
   115  
   116  func (mfm MetricFamilyMap) SumSummariesTo(name string, output *SummaryData) {
   117  	for _, m := range mfm[name].GetMetric() {
   118  		output.AddSummary(m.GetSummary())
   119  	}
   120  }
   121  
   122  func (mfm MetricFamilyMap) sumOfSingleValuesWithLabels(metric string, labelNames []string, extractFn func(*dto.Metric) float64, aggregateFn func(labelsKey string, labelValues []string, value float64)) {
   123  	metricsPerLabelValue := getMetricsWithLabelNames(mfm[metric], labelNames)
   124  
   125  	for key, mlv := range metricsPerLabelValue {
   126  		for _, m := range mlv.metrics {
   127  			val := extractFn(m)
   128  			aggregateFn(key, mlv.labelValues, val)
   129  		}
   130  	}
   131  }
   132  
   133  // MetricFamiliesPerUser is a collection of metrics gathered via calling Gatherer.Gather() method on different
   134  // gatherers, one per user.
   135  type MetricFamiliesPerUser []struct {
   136  	user    string
   137  	metrics MetricFamilyMap
   138  }
   139  
   140  func (d MetricFamiliesPerUser) GetSumOfCounters(counter string) float64 {
   141  	result := float64(0)
   142  	for _, userEntry := range d {
   143  		result += userEntry.metrics.SumCounters(counter)
   144  	}
   145  	return result
   146  }
   147  
   148  func (d MetricFamiliesPerUser) SendSumOfCounters(out chan<- prometheus.Metric, desc *prometheus.Desc, counter string) {
   149  	out <- prometheus.MustNewConstMetric(desc, prometheus.CounterValue, d.GetSumOfCounters(counter))
   150  }
   151  
   152  func (d MetricFamiliesPerUser) SendSumOfCountersWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, counter string, labelNames ...string) {
   153  	d.sumOfSingleValuesWithLabels(counter, counterValue, labelNames).WriteToMetricChannel(out, desc, prometheus.CounterValue)
   154  }
   155  
   156  func (d MetricFamiliesPerUser) SendSumOfCountersPerUser(out chan<- prometheus.Metric, desc *prometheus.Desc, counter string) {
   157  	d.SendSumOfCountersPerUserWithLabels(out, desc, counter)
   158  }
   159  
   160  // SendSumOfCountersPerUserWithLabels provides metrics with the provided label names on a per-user basis. This function assumes that `user` is the
   161  // first label on the provided metric Desc
   162  func (d MetricFamiliesPerUser) SendSumOfCountersPerUserWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, metric string, labelNames ...string) {
   163  	for _, userEntry := range d {
   164  		if userEntry.user == "" {
   165  			continue
   166  		}
   167  
   168  		result := singleValueWithLabelsMap{}
   169  		userEntry.metrics.sumOfSingleValuesWithLabels(metric, labelNames, counterValue, result.aggregateFn)
   170  		result.prependUserLabelValue(userEntry.user)
   171  		result.WriteToMetricChannel(out, desc, prometheus.CounterValue)
   172  	}
   173  }
   174  
   175  func (d MetricFamiliesPerUser) GetSumOfGauges(gauge string) float64 {
   176  	result := float64(0)
   177  	for _, userEntry := range d {
   178  		result += userEntry.metrics.SumGauges(gauge)
   179  	}
   180  	return result
   181  }
   182  
   183  func (d MetricFamiliesPerUser) SendSumOfGauges(out chan<- prometheus.Metric, desc *prometheus.Desc, gauge string) {
   184  	out <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, d.GetSumOfGauges(gauge))
   185  }
   186  
   187  func (d MetricFamiliesPerUser) SendSumOfGaugesWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, gauge string, labelNames ...string) {
   188  	d.sumOfSingleValuesWithLabels(gauge, gaugeValue, labelNames).WriteToMetricChannel(out, desc, prometheus.GaugeValue)
   189  }
   190  
   191  func (d MetricFamiliesPerUser) SendSumOfGaugesPerUser(out chan<- prometheus.Metric, desc *prometheus.Desc, gauge string) {
   192  	d.SendSumOfGaugesPerUserWithLabels(out, desc, gauge)
   193  }
   194  
   195  // SendSumOfGaugesPerUserWithLabels provides metrics with the provided label names on a per-user basis. This function assumes that `user` is the
   196  // first label on the provided metric Desc
   197  func (d MetricFamiliesPerUser) SendSumOfGaugesPerUserWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, metric string, labelNames ...string) {
   198  	for _, userEntry := range d {
   199  		if userEntry.user == "" {
   200  			continue
   201  		}
   202  
   203  		result := singleValueWithLabelsMap{}
   204  		userEntry.metrics.sumOfSingleValuesWithLabels(metric, labelNames, gaugeValue, result.aggregateFn)
   205  		result.prependUserLabelValue(userEntry.user)
   206  		result.WriteToMetricChannel(out, desc, prometheus.GaugeValue)
   207  	}
   208  }
   209  
   210  func (d MetricFamiliesPerUser) sumOfSingleValuesWithLabels(metric string, fn func(*dto.Metric) float64, labelNames []string) singleValueWithLabelsMap {
   211  	result := singleValueWithLabelsMap{}
   212  	for _, userEntry := range d {
   213  		userEntry.metrics.sumOfSingleValuesWithLabels(metric, labelNames, fn, result.aggregateFn)
   214  	}
   215  	return result
   216  }
   217  
   218  func (d MetricFamiliesPerUser) SendMaxOfGauges(out chan<- prometheus.Metric, desc *prometheus.Desc, gauge string) {
   219  	result := math.NaN()
   220  	for _, userEntry := range d {
   221  		if value := userEntry.metrics.MaxGauges(gauge); math.IsNaN(result) || value > result {
   222  			result = value
   223  		}
   224  	}
   225  
   226  	// If there's no metric, we do send 0 which is the gauge default.
   227  	if math.IsNaN(result) {
   228  		result = 0
   229  	}
   230  
   231  	out <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, result)
   232  }
   233  
   234  func (d MetricFamiliesPerUser) SendMaxOfGaugesPerUser(out chan<- prometheus.Metric, desc *prometheus.Desc, gauge string) {
   235  	for _, userEntry := range d {
   236  		if userEntry.user == "" {
   237  			continue
   238  		}
   239  
   240  		result := userEntry.metrics.MaxGauges(gauge)
   241  		out <- prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, result, userEntry.user)
   242  	}
   243  }
   244  
   245  func (d MetricFamiliesPerUser) SendSumOfSummaries(out chan<- prometheus.Metric, desc *prometheus.Desc, summaryName string) {
   246  	summaryData := SummaryData{}
   247  	for _, userEntry := range d {
   248  		userEntry.metrics.SumSummariesTo(summaryName, &summaryData)
   249  	}
   250  	out <- summaryData.Metric(desc)
   251  }
   252  
   253  func (d MetricFamiliesPerUser) SendSumOfSummariesWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, summaryName string, labelNames ...string) {
   254  	type summaryResult struct {
   255  		data        SummaryData
   256  		labelValues []string
   257  	}
   258  
   259  	result := map[string]summaryResult{}
   260  
   261  	for _, mfm := range d {
   262  		metricsPerLabelValue := getMetricsWithLabelNames(mfm.metrics[summaryName], labelNames)
   263  
   264  		for key, mwl := range metricsPerLabelValue {
   265  			for _, m := range mwl.metrics {
   266  				r := result[key]
   267  				if r.labelValues == nil {
   268  					r.labelValues = mwl.labelValues
   269  				}
   270  
   271  				r.data.AddSummary(m.GetSummary())
   272  				result[key] = r
   273  			}
   274  		}
   275  	}
   276  
   277  	for _, sr := range result {
   278  		out <- sr.data.Metric(desc, sr.labelValues...)
   279  	}
   280  }
   281  
   282  func (d MetricFamiliesPerUser) SendSumOfSummariesPerUser(out chan<- prometheus.Metric, desc *prometheus.Desc, summaryName string) {
   283  	for _, userEntry := range d {
   284  		if userEntry.user == "" {
   285  			continue
   286  		}
   287  
   288  		data := userEntry.metrics.SumSummaries(summaryName)
   289  		out <- data.Metric(desc, userEntry.user)
   290  	}
   291  }
   292  
   293  func (d MetricFamiliesPerUser) SendSumOfHistograms(out chan<- prometheus.Metric, desc *prometheus.Desc, histogramName string) {
   294  	hd := HistogramData{}
   295  	for _, userEntry := range d {
   296  		userEntry.metrics.SumHistogramsTo(histogramName, &hd)
   297  	}
   298  	out <- hd.Metric(desc)
   299  }
   300  
   301  func (d MetricFamiliesPerUser) SendSumOfHistogramsWithLabels(out chan<- prometheus.Metric, desc *prometheus.Desc, histogramName string, labelNames ...string) {
   302  	type histogramResult struct {
   303  		data        HistogramData
   304  		labelValues []string
   305  	}
   306  
   307  	result := map[string]histogramResult{}
   308  
   309  	for _, mfm := range d {
   310  		metricsPerLabelValue := getMetricsWithLabelNames(mfm.metrics[histogramName], labelNames)
   311  
   312  		for key, mwl := range metricsPerLabelValue {
   313  			for _, m := range mwl.metrics {
   314  				r := result[key]
   315  				if r.labelValues == nil {
   316  					r.labelValues = mwl.labelValues
   317  				}
   318  
   319  				r.data.AddHistogram(m.GetHistogram())
   320  				result[key] = r
   321  			}
   322  		}
   323  	}
   324  
   325  	for _, hg := range result {
   326  		out <- hg.data.Metric(desc, hg.labelValues...)
   327  	}
   328  }
   329  
   330  // struct for holding metrics with same label values
   331  type metricsWithLabels struct {
   332  	labelValues []string
   333  	metrics     []*dto.Metric
   334  }
   335  
   336  func getMetricsWithLabelNames(mf *dto.MetricFamily, labelNames []string) map[string]metricsWithLabels {
   337  	result := map[string]metricsWithLabels{}
   338  
   339  	for _, m := range mf.GetMetric() {
   340  		lbls, include := getLabelValues(m, labelNames)
   341  		if !include {
   342  			continue
   343  		}
   344  
   345  		key := getLabelsString(lbls)
   346  		r := result[key]
   347  		if r.labelValues == nil {
   348  			r.labelValues = lbls
   349  		}
   350  		r.metrics = append(r.metrics, m)
   351  		result[key] = r
   352  	}
   353  	return result
   354  }
   355  
   356  func getLabelValues(m *dto.Metric, labelNames []string) ([]string, bool) {
   357  	result := make([]string, 0, len(labelNames))
   358  
   359  	for _, ln := range labelNames {
   360  		found := false
   361  
   362  		// Look for the label among the metric ones. We re-iterate on each metric label
   363  		// which is algorithmically bad, but the main assumption is that the labelNames
   364  		// in input are typically very few units.
   365  		for _, lp := range m.GetLabel() {
   366  			if ln != lp.GetName() {
   367  				continue
   368  			}
   369  
   370  			result = append(result, lp.GetValue())
   371  			found = true
   372  			break
   373  		}
   374  
   375  		if !found {
   376  			// required labels not found
   377  			return nil, false
   378  		}
   379  	}
   380  
   381  	return result, true
   382  }
   383  
   384  func getLabelsString(labelValues []string) string {
   385  	// Get a buffer from the pool, reset it and release it at the
   386  	// end of the function.
   387  	buf := bytesBufferPool.Get().(*bytes.Buffer)
   388  	buf.Reset()
   389  	defer bytesBufferPool.Put(buf)
   390  
   391  	for _, v := range labelValues {
   392  		buf.WriteString(v)
   393  		buf.WriteByte(0) // separator, not used in prometheus labels
   394  	}
   395  	return buf.String()
   396  }
   397  
   398  // sum returns sum of values from all metrics from same metric family (= series with the same metric name, but different labels)
   399  // Supplied function extracts value.
   400  func sum(mf *dto.MetricFamily, fn func(*dto.Metric) float64) float64 {
   401  	result := float64(0)
   402  	for _, m := range mf.GetMetric() {
   403  		result += fn(m)
   404  	}
   405  	return result
   406  }
   407  
   408  // max returns the max value from all metrics from same metric family (= series with the same metric name, but different labels)
   409  // Supplied function extracts value.
   410  func max(mf *dto.MetricFamily, fn func(*dto.Metric) float64) float64 {
   411  	result := math.NaN()
   412  
   413  	for _, m := range mf.GetMetric() {
   414  		if value := fn(m); math.IsNaN(result) || value > result {
   415  			result = value
   416  		}
   417  	}
   418  
   419  	// If there's no metric, we do return 0 which is the gauge default.
   420  	if math.IsNaN(result) {
   421  		return 0
   422  	}
   423  
   424  	return result
   425  }
   426  
   427  // This works even if m is nil, m.Counter is nil or m.Counter.Value is nil (it returns 0 in those cases)
   428  func counterValue(m *dto.Metric) float64 { return m.GetCounter().GetValue() }
   429  func gaugeValue(m *dto.Metric) float64   { return m.GetGauge().GetValue() }
   430  
   431  // SummaryData keeps all data needed to create summary metric
   432  type SummaryData struct {
   433  	sampleCount uint64
   434  	sampleSum   float64
   435  	quantiles   map[float64]float64
   436  }
   437  
   438  func (s *SummaryData) AddSummary(sum *dto.Summary) {
   439  	s.sampleCount += sum.GetSampleCount()
   440  	s.sampleSum += sum.GetSampleSum()
   441  
   442  	qs := sum.GetQuantile()
   443  	if len(qs) > 0 && s.quantiles == nil {
   444  		s.quantiles = map[float64]float64{}
   445  	}
   446  
   447  	for _, q := range qs {
   448  		// we assume that all summaries have same quantiles
   449  		s.quantiles[q.GetQuantile()] += q.GetValue()
   450  	}
   451  }
   452  
   453  func (s *SummaryData) Metric(desc *prometheus.Desc, labelValues ...string) prometheus.Metric {
   454  	return prometheus.MustNewConstSummary(desc, s.sampleCount, s.sampleSum, s.quantiles, labelValues...)
   455  }
   456  
   457  // HistogramData keeps data required to build histogram Metric.
   458  type HistogramData struct {
   459  	sampleCount uint64
   460  	sampleSum   float64
   461  	buckets     map[float64]uint64
   462  }
   463  
   464  // AddHistogram adds histogram from gathered metrics to this histogram data.
   465  // Do not call this function after Metric() has been invoked, because histogram created by Metric
   466  // is using the buckets map (doesn't make a copy), and it's not allowed to change the buckets
   467  // after they've been passed to a prometheus.Metric.
   468  func (d *HistogramData) AddHistogram(histo *dto.Histogram) {
   469  	d.sampleCount += histo.GetSampleCount()
   470  	d.sampleSum += histo.GetSampleSum()
   471  
   472  	histoBuckets := histo.GetBucket()
   473  	if len(histoBuckets) > 0 && d.buckets == nil {
   474  		d.buckets = map[float64]uint64{}
   475  	}
   476  
   477  	for _, b := range histoBuckets {
   478  		// we assume that all histograms have same buckets
   479  		d.buckets[b.GetUpperBound()] += b.GetCumulativeCount()
   480  	}
   481  }
   482  
   483  // AddHistogramData merges another histogram data into this one.
   484  // Do not call this function after Metric() has been invoked, because histogram created by Metric
   485  // is using the buckets map (doesn't make a copy), and it's not allowed to change the buckets
   486  // after they've been passed to a prometheus.Metric.
   487  func (d *HistogramData) AddHistogramData(histo HistogramData) {
   488  	d.sampleCount += histo.sampleCount
   489  	d.sampleSum += histo.sampleSum
   490  
   491  	if len(histo.buckets) > 0 && d.buckets == nil {
   492  		d.buckets = map[float64]uint64{}
   493  	}
   494  
   495  	for bound, count := range histo.buckets {
   496  		// we assume that all histograms have same buckets
   497  		d.buckets[bound] += count
   498  	}
   499  }
   500  
   501  // Metric returns prometheus metric from this histogram data.
   502  //
   503  // Note that returned metric shares bucket with this HistogramData, so avoid
   504  // doing more modifications to this HistogramData after calling Metric.
   505  func (d *HistogramData) Metric(desc *prometheus.Desc, labelValues ...string) prometheus.Metric {
   506  	return prometheus.MustNewConstHistogram(desc, d.sampleCount, d.sampleSum, d.buckets, labelValues...)
   507  }
   508  
   509  // Copy returns a copy of this histogram data.
   510  func (d *HistogramData) Copy() *HistogramData {
   511  	cp := &HistogramData{}
   512  	cp.AddHistogramData(*d)
   513  	return cp
   514  }
   515  
   516  // NewHistogramDataCollector creates new histogram data collector.
   517  func NewHistogramDataCollector(desc *prometheus.Desc) *HistogramDataCollector {
   518  	return &HistogramDataCollector{
   519  		desc: desc,
   520  		data: &HistogramData{},
   521  	}
   522  }
   523  
   524  // HistogramDataCollector combines histogram data, with prometheus descriptor. It can be registered
   525  // into prometheus to report histogram with stored data. Data can be updated via Add method.
   526  type HistogramDataCollector struct {
   527  	desc *prometheus.Desc
   528  
   529  	dataMu sync.RWMutex
   530  	data   *HistogramData
   531  }
   532  
   533  func (h *HistogramDataCollector) Describe(out chan<- *prometheus.Desc) {
   534  	out <- h.desc
   535  }
   536  
   537  func (h *HistogramDataCollector) Collect(out chan<- prometheus.Metric) {
   538  	h.dataMu.RLock()
   539  	defer h.dataMu.RUnlock()
   540  
   541  	// We must create a copy of the HistogramData data structure before calling Metric()
   542  	// to honor its contract.
   543  	out <- h.data.Copy().Metric(h.desc)
   544  }
   545  
   546  func (h *HistogramDataCollector) Add(hd HistogramData) {
   547  	h.dataMu.Lock()
   548  	defer h.dataMu.Unlock()
   549  
   550  	h.data.AddHistogramData(hd)
   551  }
   552  
   553  // UserRegistry holds a Prometheus registry associated to a specific user.
   554  type UserRegistry struct {
   555  	user string               // Set to "" when registry is soft-removed.
   556  	reg  *prometheus.Registry // Set to nil, when registry is soft-removed.
   557  
   558  	// Set to last result of Gather() call when removing registry.
   559  	lastGather MetricFamilyMap
   560  }
   561  
   562  // UserRegistries holds Prometheus registries for multiple users, guaranteeing
   563  // multi-thread safety and stable ordering.
   564  type UserRegistries struct {
   565  	regsMu sync.Mutex
   566  	regs   []UserRegistry
   567  }
   568  
   569  // NewUserRegistries makes new UserRegistries.
   570  func NewUserRegistries() *UserRegistries {
   571  	return &UserRegistries{}
   572  }
   573  
   574  // AddUserRegistry adds an user registry. If user already has a registry,
   575  // previous registry is removed, but latest metric values are preserved
   576  // in order to avoid counter resets.
   577  func (r *UserRegistries) AddUserRegistry(user string, reg *prometheus.Registry) {
   578  	r.regsMu.Lock()
   579  	defer r.regsMu.Unlock()
   580  
   581  	// Soft-remove user registry, if user has one already.
   582  	for idx := 0; idx < len(r.regs); {
   583  		if r.regs[idx].user != user {
   584  			idx++
   585  			continue
   586  		}
   587  
   588  		if r.softRemoveUserRegistry(&r.regs[idx]) {
   589  			// Keep it.
   590  			idx++
   591  		} else {
   592  			// Remove it.
   593  			r.regs = append(r.regs[:idx], r.regs[idx+1:]...)
   594  		}
   595  	}
   596  
   597  	// New registries must be added to the end of the list, to guarantee stability.
   598  	r.regs = append(r.regs, UserRegistry{
   599  		user: user,
   600  		reg:  reg,
   601  	})
   602  }
   603  
   604  // RemoveUserRegistry removes all Prometheus registries for a given user.
   605  // If hard is true, registry is removed completely.
   606  // If hard is false, latest registry values are preserved for future aggregations.
   607  func (r *UserRegistries) RemoveUserRegistry(user string, hard bool) {
   608  	r.regsMu.Lock()
   609  	defer r.regsMu.Unlock()
   610  
   611  	for idx := 0; idx < len(r.regs); {
   612  		if user != r.regs[idx].user {
   613  			idx++
   614  			continue
   615  		}
   616  
   617  		if !hard && r.softRemoveUserRegistry(&r.regs[idx]) {
   618  			idx++ // keep it
   619  		} else {
   620  			r.regs = append(r.regs[:idx], r.regs[idx+1:]...) // remove it.
   621  		}
   622  	}
   623  }
   624  
   625  // Returns true, if we should keep latest metrics. Returns false if we failed to gather latest metrics,
   626  // and this can be removed completely.
   627  func (r *UserRegistries) softRemoveUserRegistry(ur *UserRegistry) bool {
   628  	last, err := ur.reg.Gather()
   629  	if err != nil {
   630  		level.Warn(util_log.Logger).Log("msg", "failed to gather metrics from registry", "user", ur.user, "err", err)
   631  		return false
   632  	}
   633  
   634  	for ix := 0; ix < len(last); {
   635  		// Only keep metrics for which we don't want to go down, since that indicates reset (counter, summary, histogram).
   636  		switch last[ix].GetType() {
   637  		case dto.MetricType_COUNTER, dto.MetricType_SUMMARY, dto.MetricType_HISTOGRAM:
   638  			ix++
   639  		default:
   640  			// Remove gauges and unknowns.
   641  			last = append(last[:ix], last[ix+1:]...)
   642  		}
   643  	}
   644  
   645  	// No metrics left.
   646  	if len(last) == 0 {
   647  		return false
   648  	}
   649  
   650  	ur.lastGather, err = NewMetricFamilyMap(last)
   651  	if err != nil {
   652  		level.Warn(util_log.Logger).Log("msg", "failed to gather metrics from registry", "user", ur.user, "err", err)
   653  		return false
   654  	}
   655  
   656  	ur.user = ""
   657  	ur.reg = nil
   658  	return true
   659  }
   660  
   661  // Registries returns a copy of the user registries list.
   662  func (r *UserRegistries) Registries() []UserRegistry {
   663  	r.regsMu.Lock()
   664  	defer r.regsMu.Unlock()
   665  
   666  	out := make([]UserRegistry, 0, len(r.regs))
   667  	out = append(out, r.regs...)
   668  
   669  	return out
   670  }
   671  
   672  func (r *UserRegistries) BuildMetricFamiliesPerUser() MetricFamiliesPerUser {
   673  	data := MetricFamiliesPerUser{}
   674  	for _, entry := range r.Registries() {
   675  		// Set for removed users.
   676  		if entry.reg == nil {
   677  			if entry.lastGather != nil {
   678  				data = append(data, struct {
   679  					user    string
   680  					metrics MetricFamilyMap
   681  				}{user: "", metrics: entry.lastGather})
   682  			}
   683  
   684  			continue
   685  		}
   686  
   687  		m, err := entry.reg.Gather()
   688  		if err == nil {
   689  			var mfm MetricFamilyMap // := would shadow err from outer block, and single err check will not work
   690  			mfm, err = NewMetricFamilyMap(m)
   691  			if err == nil {
   692  				data = append(data, struct {
   693  					user    string
   694  					metrics MetricFamilyMap
   695  				}{
   696  					user:    entry.user,
   697  					metrics: mfm,
   698  				})
   699  			}
   700  		}
   701  
   702  		if err != nil {
   703  			level.Warn(util_log.Logger).Log("msg", "failed to gather metrics from registry", "user", entry.user, "err", err)
   704  			continue
   705  		}
   706  	}
   707  	return data
   708  }
   709  
   710  // FromLabelPairsToLabels converts dto.LabelPair into labels.Labels.
   711  func FromLabelPairsToLabels(pairs []*dto.LabelPair) labels.Labels {
   712  	builder := labels.NewBuilder(nil)
   713  	for _, pair := range pairs {
   714  		builder.Set(pair.GetName(), pair.GetValue())
   715  	}
   716  	return builder.Labels()
   717  }
   718  
   719  // GetSumOfHistogramSampleCount returns the sum of samples count of histograms matching the provided metric name
   720  // and optional label matchers. Returns 0 if no metric matches.
   721  func GetSumOfHistogramSampleCount(families []*dto.MetricFamily, metricName string, matchers labels.Selector) uint64 {
   722  	sum := uint64(0)
   723  
   724  	for _, metric := range families {
   725  		if metric.GetName() != metricName {
   726  			continue
   727  		}
   728  
   729  		if metric.GetType() != dto.MetricType_HISTOGRAM {
   730  			continue
   731  		}
   732  
   733  		for _, series := range metric.GetMetric() {
   734  			if !matchers.Matches(FromLabelPairsToLabels(series.GetLabel())) {
   735  				continue
   736  			}
   737  
   738  			histogram := series.GetHistogram()
   739  			sum += histogram.GetSampleCount()
   740  		}
   741  	}
   742  
   743  	return sum
   744  }
   745  
   746  // GetLables returns list of label combinations used by this collector at the time of call.
   747  // This can be used to find and delete unused metrics.
   748  func GetLabels(c prometheus.Collector, filter map[string]string) ([]labels.Labels, error) {
   749  	ch := make(chan prometheus.Metric, 16)
   750  
   751  	go func() {
   752  		defer close(ch)
   753  		c.Collect(ch)
   754  	}()
   755  
   756  	errs := tsdb_errors.NewMulti()
   757  	var result []labels.Labels
   758  	dtoMetric := &dto.Metric{}
   759  	lbls := labels.NewBuilder(nil)
   760  
   761  nextMetric:
   762  	for m := range ch {
   763  		err := m.Write(dtoMetric)
   764  		if err != nil {
   765  			errs.Add(err)
   766  			// We cannot return here, to avoid blocking goroutine calling c.Collect()
   767  			continue
   768  		}
   769  
   770  		lbls.Reset(nil)
   771  		for _, lp := range dtoMetric.Label {
   772  			n := lp.GetName()
   773  			v := lp.GetValue()
   774  
   775  			filterValue, ok := filter[n]
   776  			if ok && filterValue != v {
   777  				continue nextMetric
   778  			}
   779  
   780  			lbls.Set(lp.GetName(), lp.GetValue())
   781  		}
   782  		result = append(result, lbls.Labels())
   783  	}
   784  
   785  	return result, errs.Err()
   786  }
   787  
   788  // DeleteMatchingLabels removes metric with labels matching the filter.
   789  func DeleteMatchingLabels(c CollectorVec, filter map[string]string) error {
   790  	lbls, err := GetLabels(c, filter)
   791  	if err != nil {
   792  		return err
   793  	}
   794  
   795  	for _, ls := range lbls {
   796  		c.Delete(ls.Map())
   797  	}
   798  
   799  	return nil
   800  }
   801  
   802  // CollectorVec is a collector that can delete metrics by labels.
   803  // Implemented by *prometheus.MetricVec (used by CounterVec, GaugeVec, SummaryVec, and HistogramVec).
   804  type CollectorVec interface {
   805  	prometheus.Collector
   806  	Delete(labels prometheus.Labels) bool
   807  }