diff --git a/prometheus/examples_test.go b/prometheus/examples_test.go index a73ed184c..6645de56b 100644 --- a/prometheus/examples_test.go +++ b/prometheus/examples_test.go @@ -16,6 +16,7 @@ package prometheus_test import ( "bytes" "fmt" + "google.golang.org/protobuf/types/known/timestamppb" "math" "net/http" "runtime" @@ -549,11 +550,27 @@ func ExampleNewConstHistogram() { prometheus.Labels{"owner": "example"}, ) + var exemplars []*dto.Exemplar + n := "testName" + v := "testVal" + lp := dto.LabelPair{Name: &n, Value: &v} + var labelPairs []*dto.LabelPair + labelPairs = append(labelPairs, &lp) + val := float64(42) + t, _ := time.Parse("unix", "Mon Jan _2 15:04:05 MST 2006") + ts := timestamppb.New(t) + + for i := 0; i < 4; i++ { + e := dto.Exemplar{Label: labelPairs, Value: &val, Timestamp: ts} + exemplars = append(exemplars, &e) + } + // Create a constant histogram from values we got from a 3rd party telemetry system. - h := prometheus.MustNewConstHistogram( + h := prometheus.MustNewConstHistogramWithExemplar( desc, 4711, 403.34, map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, + exemplars, "200", "get", ) @@ -583,18 +600,58 @@ func ExampleNewConstHistogram() { // bucket: < // cumulative_count: 121 // upper_bound: 25 + // exemplar: < + // label: < + // name: "testName" + // value: "testVal" + // > + // value: 42 + // timestamp: < + // seconds: -62135596800 + // > + // > // > // bucket: < // cumulative_count: 2403 // upper_bound: 50 + // exemplar: < + // label: < + // name: "testName" + // value: "testVal" + // > + // value: 42 + // timestamp: < + // seconds: -62135596800 + // > + // > // > // bucket: < // cumulative_count: 3221 // upper_bound: 100 + // exemplar: < + // label: < + // name: "testName" + // value: "testVal" + // > + // value: 42 + // timestamp: < + // seconds: -62135596800 + // > + // > // > // bucket: < // cumulative_count: 4233 // upper_bound: 200 + // exemplar: < + // label: < + // name: "testName" + // value: "testVal" + // > + // value: 42 + // timestamp: < + // seconds: -62135596800 + // > + // > // > // > } diff --git a/prometheus/histogram.go b/prometheus/histogram.go index 893802fd6..b4440746d 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -573,6 +573,7 @@ type constHistogram struct { sum float64 buckets map[float64]uint64 labelPairs []*dto.LabelPair + exemplars []*dto.Exemplar } func (h *constHistogram) Desc() *Desc { @@ -585,7 +586,6 @@ func (h *constHistogram) Write(out *dto.Metric) error { his.SampleCount = proto.Uint64(h.count) his.SampleSum = proto.Float64(h.sum) - for upperBound, count := range h.buckets { buckets = append(buckets, &dto.Bucket{ CumulativeCount: proto.Uint64(count), @@ -596,6 +596,13 @@ func (h *constHistogram) Write(out *dto.Metric) error { if len(buckets) > 0 { sort.Sort(buckSort(buckets)) } + + if len(h.exemplars) > 0 { + for i := 0; i < len(buckets); i++ { + buckets[i].Exemplar = h.exemplars[i] + } + } + his.Bucket = buckets out.Histogram = his @@ -604,6 +611,13 @@ func (h *constHistogram) Write(out *dto.Metric) error { return nil } +func (h *constHistogram) GetExemplars() []*dto.Exemplar { + if h != nil { + return h.exemplars + } + return nil +} + // NewConstHistogram returns a metric representing a Prometheus histogram with // fixed values for the count, sum, and bucket counts. As those parameters // cannot be changed, the returned value does not implement the Histogram @@ -617,6 +631,7 @@ func (h *constHistogram) Write(out *dto.Metric) error { // // NewConstHistogram returns an error if the length of labelValues is not // consistent with the variable labels in Desc or if Desc is invalid. + func NewConstHistogram( desc *Desc, count uint64, @@ -655,6 +670,48 @@ func MustNewConstHistogram( return m } +func NewConstHistogramWithExemplar( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + exemplars []*dto.Exemplar, + labelValues ...string, + +) (Metric, error) { + if desc.err != nil { + return nil, desc.err + } + if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { + return nil, err + } + return &constHistogram{ + desc: desc, + count: count, + sum: sum, + buckets: buckets, + exemplars: exemplars, + labelPairs: MakeLabelPairs(desc, labelValues), + }, nil +} + +// MustNewConstHistogram is a version of NewConstHistogram that panics where +// NewConstHistogram would have returned an error. +func MustNewConstHistogramWithExemplar( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + exemplars []*dto.Exemplar, + labelValues ...string, +) Metric { + m, err := NewConstHistogramWithExemplar(desc, count, sum, buckets, exemplars, labelValues...) + if err != nil { + panic(err) + } + return m +} + type buckSort []*dto.Bucket func (s buckSort) Len() int {