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 }