github.com/thanos-io/thanos@v0.32.5/internal/cortex/util/metrics_helper.go (about)

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