@@ -454,14 +454,17 @@ func (s *SummaryData) Metric(desc *prometheus.Desc, labelValues ...string) prome
454454 return prometheus .MustNewConstSummary (desc , s .sampleCount , s .sampleSum , s .quantiles , labelValues ... )
455455}
456456
457- // HistogramData keeps data required to build histogram Metric
457+ // HistogramData keeps data required to build histogram Metric.
458458type HistogramData struct {
459459 sampleCount uint64
460460 sampleSum float64
461461 buckets map [float64 ]uint64
462462}
463463
464- // Adds histogram from gathered metrics to this histogram data.
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.
465468func (d * HistogramData ) AddHistogram (histo * dto.Histogram ) {
466469 d .sampleCount += histo .GetSampleCount ()
467470 d .sampleSum += histo .GetSampleSum ()
@@ -477,7 +480,10 @@ func (d *HistogramData) AddHistogram(histo *dto.Histogram) {
477480 }
478481}
479482
480- // Merged another histogram data into this one.
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.
481487func (d * HistogramData ) AddHistogramData (histo HistogramData ) {
482488 d .sampleCount += histo .sampleCount
483489 d .sampleSum += histo .sampleSum
@@ -492,12 +498,22 @@ func (d *HistogramData) AddHistogramData(histo HistogramData) {
492498 }
493499}
494500
495- // Return prometheus metric from this histogram data.
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.
496505func (d * HistogramData ) Metric (desc * prometheus.Desc , labelValues ... string ) prometheus.Metric {
497506 return prometheus .MustNewConstHistogram (desc , d .sampleCount , d .sampleSum , d .buckets , labelValues ... )
498507}
499508
500- // Creates new histogram data collector.
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.
501517func NewHistogramDataCollector (desc * prometheus.Desc ) * HistogramDataCollector {
502518 return & HistogramDataCollector {
503519 desc : desc ,
@@ -522,7 +538,9 @@ func (h *HistogramDataCollector) Collect(out chan<- prometheus.Metric) {
522538 h .dataMu .RLock ()
523539 defer h .dataMu .RUnlock ()
524540
525- out <- h .data .Metric (h .desc )
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 )
526544}
527545
528546func (h * HistogramDataCollector ) Add (hd HistogramData ) {
0 commit comments