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 }