From 6a6a456854a75b32c00f3b7cb1e8d9c4f9a71dce Mon Sep 17 00:00:00 2001 From: Shivanth Date: Sun, 17 Nov 2024 20:29:39 +0100 Subject: [PATCH 01/13] Add exemplars for native histograms Remove unused variable fix tests Signed-off-by: Shivanth --- prometheus/metric.go | 6 ++++++ prometheus/metric_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/prometheus/metric.go b/prometheus/metric.go index 592eec3e2..d8e3d2c13 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -187,6 +187,12 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { pb.Counter.Exemplar = m.exemplars[len(m.exemplars)-1] case pb.Histogram != nil: for _, e := range m.exemplars { + if pb.Histogram.Schema != nil { + if *pb.Histogram.Schema > math.MinInt32 && e.GetTimestamp() != nil { + pb.Histogram.Exemplars = append(pb.Histogram.Exemplars, e) + } + continue + } // pb.Histogram.Bucket are sorted by UpperBound. i := sort.Search(len(pb.Histogram.Bucket), func(i int) bool { return pb.Histogram.Bucket[i].GetUpperBound() >= e.GetValue() diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 629aa4f84..afdec0ae0 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -16,10 +16,12 @@ package prometheus import ( "math" "testing" + "time" dto "github.com/prometheus/client_model/go" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) func TestBuildFQName(t *testing.T) { @@ -90,3 +92,29 @@ func TestWithExemplarsMetric(t *testing.T) { } }) } + +func TestWithExemplarsNativeHistogramMetric(t *testing.T) { + t.Run("histogram", func(t *testing.T) { + // Create a constant histogram from values we got from a 3rd party telemetry system. + h := MustNewConstNativeHistogram( + NewDesc("http_request_duration_seconds", "A histogram of the HTTP request durations.", nil, nil), + 10, 12.1, map[int]int64{1: 7, 2: 1, 3: 2}, map[int]int64{}, 0, 2, 0.2, time.Date( + 2009, 11, 17, 20, 34, 58, 651387237, time.UTC)) + m := &withExemplarsMetric{Metric: h, exemplars: []*dto.Exemplar{ + {Value: proto.Float64(2000.0), Timestamp: timestamppb.New(time.Date(2009, 11, 17, 20, 34, 58, 3243244, time.UTC))}, + }} + metric := dto.Metric{} + if err := m.Write(&metric); err != nil { + t.Fatal(err) + } + if want, got := 1, len(metric.GetHistogram().Exemplars); want != got { + t.Errorf("want %v, got %v", want, got) + } + + for _, b := range metric.GetHistogram().Bucket { + if b.Exemplar != nil { + t.Error("Not expecting exemplar for bucket") + } + } + }) +} From 67dbd86c8880d189c82567659ec593d0eca840bd Mon Sep 17 00:00:00 2001 From: Shivanth Date: Tue, 14 Jan 2025 15:50:22 +0100 Subject: [PATCH 02/13] Add doc string for NewMetricWithExemplars Signed-off-by: Shivanth --- prometheus/metric.go | 1 + 1 file changed, 1 insertion(+) diff --git a/prometheus/metric.go b/prometheus/metric.go index d8e3d2c13..6e0b0339e 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -233,6 +233,7 @@ type Exemplar struct { // Only last applicable exemplar is injected from the list. // For example for Counter it means last exemplar is injected. // For Histogram, it means last applicable exemplar for each bucket is injected. +// Note that for a nativeHistogram, all valid exemplars are injected. // // NewMetricWithExemplars works best with MustNewConstMetric and // MustNewConstHistogram, see example. From f799a31efa057128ab1de91efd4ef59f95353c58 Mon Sep 17 00:00:00 2001 From: Shivanth MP Date: Tue, 4 Feb 2025 13:03:54 +0100 Subject: [PATCH 03/13] Update prometheus/metric.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Rabenstein Signed-off-by: Shivanth MP --- prometheus/metric.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 6e0b0339e..5a21cb5a9 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -191,7 +191,10 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { if *pb.Histogram.Schema > math.MinInt32 && e.GetTimestamp() != nil { pb.Histogram.Exemplars = append(pb.Histogram.Exemplars, e) } - continue + if len(pb.Histogram.Bucket) == 0 { + // Don't proceed to classic buckets if there are none. + continue + } } // pb.Histogram.Bucket are sorted by UpperBound. i := sort.Search(len(pb.Histogram.Bucket), func(i int) bool { From 2ec908193cc0b6095514857fa303294852c9a51a Mon Sep 17 00:00:00 2001 From: Shivanth MP Date: Tue, 4 Feb 2025 13:04:07 +0100 Subject: [PATCH 04/13] Update prometheus/metric.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Rabenstein Signed-off-by: Shivanth MP --- prometheus/metric.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 5a21cb5a9..4ce74f51f 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -236,7 +236,7 @@ type Exemplar struct { // Only last applicable exemplar is injected from the list. // For example for Counter it means last exemplar is injected. // For Histogram, it means last applicable exemplar for each bucket is injected. -// Note that for a nativeHistogram, all valid exemplars are injected. +// For a Native Histogram, all valid exemplars are injected. // // NewMetricWithExemplars works best with MustNewConstMetric and // MustNewConstHistogram, see example. From 6a3648ef7c1c1f0e4e98c3f77a6b97419048e8fa Mon Sep 17 00:00:00 2001 From: Shivanth Date: Thu, 6 Feb 2025 13:01:19 +0100 Subject: [PATCH 05/13] Add more tests Signed-off-by: Shivanth --- prometheus/metric_test.go | 52 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index afdec0ae0..649f6d764 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -94,7 +94,30 @@ func TestWithExemplarsMetric(t *testing.T) { } func TestWithExemplarsNativeHistogramMetric(t *testing.T) { - t.Run("histogram", func(t *testing.T) { + t.Run("native histogram single exemplar", func(t *testing.T) { + // Create a constant histogram from values we got from a 3rd party telemetry system. + h := MustNewConstNativeHistogram( + NewDesc("http_request_duration_seconds", "A histogram of the HTTP request durations.", nil, nil), + 10, 12.1, map[int]int64{1: 7, 2: 1, 3: 2}, map[int]int64{}, 0, 2, 0.2, time.Date( + 2009, 11, 17, 20, 34, 58, 651387237, time.UTC)) + m := &withExemplarsMetric{Metric: h, exemplars: []*dto.Exemplar{ + {Value: proto.Float64(2000.0), Timestamp: timestamppb.New(time.Date(2009, 11, 17, 20, 34, 58, 3243244, time.UTC))}, + }} + metric := dto.Metric{} + if err := m.Write(&metric); err != nil { + t.Fatal(err) + } + if want, got := 1, len(metric.GetHistogram().Exemplars); want != got { + t.Errorf("want %v, got %v", want, got) + } + + for _, b := range metric.GetHistogram().Bucket { + if b.Exemplar != nil { + t.Error("Not expecting exemplar for bucket") + } + } + }) + t.Run("native histogram multiple exemplar", func(t *testing.T) { // Create a constant histogram from values we got from a 3rd party telemetry system. h := MustNewConstNativeHistogram( NewDesc("http_request_duration_seconds", "A histogram of the HTTP request durations.", nil, nil), @@ -102,14 +125,41 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { 2009, 11, 17, 20, 34, 58, 651387237, time.UTC)) m := &withExemplarsMetric{Metric: h, exemplars: []*dto.Exemplar{ {Value: proto.Float64(2000.0), Timestamp: timestamppb.New(time.Date(2009, 11, 17, 20, 34, 58, 3243244, time.UTC))}, + {Value: proto.Float64(1000.0), Timestamp: timestamppb.New(time.Date(2009, 11, 17, 20, 34, 59, 3243244, time.UTC))}, }} metric := dto.Metric{} if err := m.Write(&metric); err != nil { t.Fatal(err) } + if want, got := 2, len(metric.GetHistogram().Exemplars); want != got { + t.Errorf("want %v, got %v", want, got) + } + + for _, b := range metric.GetHistogram().Bucket { + if b.Exemplar != nil { + t.Error("Not expecting exemplar for bucket") + } + } + }) + t.Run("native histogram exemplar without timestamp", func(t *testing.T) { + // Create a constant histogram from values we got from a 3rd party telemetry system. + h := MustNewConstNativeHistogram( + NewDesc("http_request_duration_seconds", "A histogram of the HTTP request durations.", nil, nil), + 10, 12.1, map[int]int64{1: 7, 2: 1, 3: 2}, map[int]int64{}, 0, 2, 0.2, time.Date( + 2009, 11, 17, 20, 34, 58, 651387237, time.UTC)) + m := MustNewMetricWithExemplars(h, Exemplar{ + Value: 1000.0, + }) + metric := dto.Metric{} + if err := m.Write(&metric); err != nil { + t.Fatal(err) + } if want, got := 1, len(metric.GetHistogram().Exemplars); want != got { t.Errorf("want %v, got %v", want, got) } + if got := metric.GetHistogram().Exemplars[0].Timestamp; got == nil { + t.Errorf("Got nil timestamp") + } for _, b := range metric.GetHistogram().Bucket { if b.Exemplar != nil { From 7f9f76dd0a1be210f500ab827a4c47ee59eb75d8 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Mon, 14 Apr 2025 23:12:59 +0200 Subject: [PATCH 06/13] nativehistogram exemplar test Signed-off-by: Shivanth --- prometheus/metric_test.go | 189 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 649f6d764..75d8855ec 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -14,6 +14,8 @@ package prometheus import ( + "errors" + "fmt" "math" "testing" "time" @@ -167,4 +169,191 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { } } }) + t.Run("nativehistogram metric exemplars should be available in both buckets and exemplars", func(t *testing.T) { + now := time.Now() + tcs := []struct { + Name string + Count uint64 + Sum float64 + PositiveBuckets map[int]int64 + NegativeBuckets map[int]int64 + ZeroBucket uint64 + NativeHistogramSchema int32 + NativeHistogramZeroThreshold float64 + CreatedTimestamp time.Time + Bucket []*dto.Bucket + Exemplars []Exemplar + Want *dto.Metric + }{ + { + Name: "nativehistogram metric exemplars should be available in both buckets and exemplars", + Count: 6, + Sum: 7.4, + PositiveBuckets: map[int]int64{ + 0: 1, 2: 2, 4: 2, + }, + NegativeBuckets: map[int]int64{}, + ZeroBucket: 1, + + NativeHistogramSchema: 2, + NativeHistogramZeroThreshold: 2.938735877055719e-39, + CreatedTimestamp: now, + Bucket: []*dto.Bucket{ + { + CumulativeCount: PointOf(uint64(6)), + UpperBound: PointOf(float64(1)), + }, + { + CumulativeCount: PointOf(uint64(8)), + UpperBound: PointOf(float64(2)), + }, + { + CumulativeCount: PointOf(uint64(11)), + UpperBound: PointOf(float64(5)), + }, + { + CumulativeCount: PointOf(uint64(13)), + UpperBound: PointOf(float64(10)), + }, + }, + Exemplars: []Exemplar{ + { + Timestamp: now, + Value: 10, + }, + }, + Want: &dto.Metric{ + Histogram: &dto.Histogram{ + SampleCount: proto.Uint64(6), + SampleSum: proto.Float64(7.4), + Schema: proto.Int32(2), + ZeroThreshold: proto.Float64(2.938735877055719e-39), + ZeroCount: proto.Uint64(1), + PositiveSpan: []*dto.BucketSpan{ + {Offset: proto.Int32(0), Length: proto.Uint32(5)}, + }, + PositiveDelta: []int64{1, -1, 2, -2, 2}, + Exemplars: []*dto.Exemplar{ + { + Value: PointOf(float64(10)), + Timestamp: timestamppb.New(now), + }, + }, + Bucket: []*dto.Bucket{ + { + CumulativeCount: PointOf(uint64(6)), + UpperBound: PointOf(float64(1)), + }, + { + CumulativeCount: PointOf(uint64(8)), + UpperBound: PointOf(float64(2)), + }, + { + CumulativeCount: PointOf(uint64(11)), + UpperBound: PointOf(float64(5)), + }, + { + CumulativeCount: PointOf(uint64(13)), + UpperBound: PointOf(float64(10)), + Exemplar: &dto.Exemplar{ + Timestamp: timestamppb.New(now), + Value: PointOf(float64(10)), + }, + }, + }, + CreatedTimestamp: timestamppb.New(now), + }, + }, + }, + } + + for _, tc := range tcs { + m, err := NewDummyConstNativeHistogram(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) + if err != nil { + fmt.Println(err) + t.Fail() + } + metricWithExemplar, err := NewMetricWithExemplars(m, tc.Exemplars[0]) + if err != nil { + fmt.Println(err) + t.Fail() + } + got := &dto.Metric{} + err = metricWithExemplar.Write(got) + if err != nil { + fmt.Println(err) + t.Fail() + } + + if !proto.Equal(tc.Want, got) { + t.Errorf("want histogram %q, got %q", tc.Want, got) + } + + } + }) +} + +func PointOf[T any](value T) *T { + return &value +} + +// This is a dummy Histogram which has both buckets and nativehistogram +// to test metrics with exemplars +func NewDummyConstNativeHistogram( + desc *Desc, + count uint64, + sum float64, + positiveBuckets, negativeBuckets map[int]int64, + zeroBucket uint64, + schema int32, + zeroThreshold float64, + createdTimestamp time.Time, + // DummyNativeHistogram also defines buckets in the metric for testing + buckets []*dto.Bucket, + labelValues ...string, +) (Metric, error) { + if desc.err != nil { + return nil, desc.err + } + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { + return nil, err + } + if schema > nativeHistogramSchemaMaximum || schema < nativeHistogramSchemaMinimum { + return nil, errors.New("invalid native histogram schema") + } + if err := validateCount(sum, count, negativeBuckets, positiveBuckets, zeroBucket); err != nil { + return nil, err + } + + NegativeSpan, NegativeDelta := makeBucketsFromMap(negativeBuckets) + PositiveSpan, PositiveDelta := makeBucketsFromMap(positiveBuckets) + ret := &constNativeHistogram{ + desc: desc, + Histogram: dto.Histogram{ + CreatedTimestamp: timestamppb.New(createdTimestamp), + Schema: &schema, + ZeroThreshold: &zeroThreshold, + SampleCount: &count, + SampleSum: &sum, + + NegativeSpan: NegativeSpan, + NegativeDelta: NegativeDelta, + + PositiveSpan: PositiveSpan, + PositiveDelta: PositiveDelta, + + ZeroCount: proto.Uint64(zeroBucket), + + // DummyNativeHistogram also defines buckets in the metric + Bucket: buckets, + }, + labelPairs: MakeLabelPairs(desc, labelValues), + } + if *ret.ZeroThreshold == 0 && *ret.ZeroCount == 0 && len(ret.PositiveSpan) == 0 && len(ret.NegativeSpan) == 0 { + ret.PositiveSpan = []*dto.BucketSpan{{ + Offset: proto.Int32(0), + Length: proto.Uint32(0), + }} + } + return ret, nil } From ca2462c0fd860b7677ae593bf366d5e57dffdd34 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Tue, 15 Apr 2025 09:24:50 +0200 Subject: [PATCH 07/13] Remove printlns Signed-off-by: Shivanth --- prometheus/metric_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 75d8855ec..068097369 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -15,7 +15,6 @@ package prometheus import ( "errors" - "fmt" "math" "testing" "time" @@ -270,18 +269,15 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { for _, tc := range tcs { m, err := NewDummyConstNativeHistogram(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) if err != nil { - fmt.Println(err) t.Fail() } metricWithExemplar, err := NewMetricWithExemplars(m, tc.Exemplars[0]) if err != nil { - fmt.Println(err) t.Fail() } got := &dto.Metric{} err = metricWithExemplar.Write(got) if err != nil { - fmt.Println(err) t.Fail() } From 474af4807f49cd5ee5251d76628ee57af9f4c5a2 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Wed, 16 Apr 2025 22:27:28 +0200 Subject: [PATCH 08/13] Change the check for native histograms in proto Signed-off-by: Shivanth --- prometheus/metric.go | 25 ++++++++++++++----------- prometheus/metric_test.go | 12 +++++++----- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 4ce74f51f..05fbf084b 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -186,30 +186,33 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { case pb.Counter != nil: pb.Counter.Exemplar = m.exemplars[len(m.exemplars)-1] case pb.Histogram != nil: + h := pb.Histogram for _, e := range m.exemplars { - if pb.Histogram.Schema != nil { - if *pb.Histogram.Schema > math.MinInt32 && e.GetTimestamp() != nil { - pb.Histogram.Exemplars = append(pb.Histogram.Exemplars, e) + if ((h.ZeroThreshold != nil && *h.ZeroThreshold != 0) || + (h.ZeroCount != nil && *h.ZeroCount != 0) || len(h.PositiveSpan) != 0 || + len(h.NegativeSpan) != 0) && e.GetTimestamp() != nil { + if h.Schema != nil { + h.Exemplars = append(h.Exemplars, e) } - if len(pb.Histogram.Bucket) == 0 { + if len(h.Bucket) == 0 { // Don't proceed to classic buckets if there are none. continue } } - // pb.Histogram.Bucket are sorted by UpperBound. - i := sort.Search(len(pb.Histogram.Bucket), func(i int) bool { - return pb.Histogram.Bucket[i].GetUpperBound() >= e.GetValue() + // h.Bucket are sorted by UpperBound. + i := sort.Search(len(h.Bucket), func(i int) bool { + return h.Bucket[i].GetUpperBound() >= e.GetValue() }) - if i < len(pb.Histogram.Bucket) { - pb.Histogram.Bucket[i].Exemplar = e + if i < len(h.Bucket) { + h.Bucket[i].Exemplar = e } else { // The +Inf bucket should be explicitly added if there is an exemplar for it, similar to non-const histogram logic in https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L357-L365. b := &dto.Bucket{ - CumulativeCount: proto.Uint64(pb.Histogram.GetSampleCount()), + CumulativeCount: proto.Uint64(h.GetSampleCount()), UpperBound: proto.Float64(math.Inf(1)), Exemplar: e, } - pb.Histogram.Bucket = append(pb.Histogram.Bucket, b) + h.Bucket = append(h.Bucket, b) } } default: diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 068097369..50187bc9a 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -15,6 +15,7 @@ package prometheus import ( "errors" + "fmt" "math" "testing" "time" @@ -185,7 +186,7 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { Want *dto.Metric }{ { - Name: "nativehistogram metric exemplars should be available in both buckets and exemplars", + Name: "test_metric", Count: 6, Sum: 7.4, PositiveBuckets: map[int]int64{ @@ -267,7 +268,7 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { } for _, tc := range tcs { - m, err := NewDummyConstNativeHistogram(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) + m, err := newDummyConstNativeHistogram(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) if err != nil { t.Fail() } @@ -293,9 +294,9 @@ func PointOf[T any](value T) *T { return &value } -// This is a dummy Histogram which has both buckets and nativehistogram -// to test metrics with exemplars -func NewDummyConstNativeHistogram( +// newNativeHistogramWithClassicBuckets returns a Metric representing +// a native histogram that also has classic buckets. This is for testing purposes. +func newDummyConstNativeHistogram( desc *Desc, count uint64, sum float64, @@ -309,6 +310,7 @@ func NewDummyConstNativeHistogram( labelValues ...string, ) (Metric, error) { if desc.err != nil { + fmt.Println("error", desc.err) return nil, desc.err } if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { From 3940ed0ecd09dcf9da221f5b77c6b52e54060113 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Wed, 16 Apr 2025 22:31:23 +0200 Subject: [PATCH 09/13] rename function Signed-off-by: Shivanth --- prometheus/metric_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prometheus/metric_test.go b/prometheus/metric_test.go index 50187bc9a..f6553c332 100644 --- a/prometheus/metric_test.go +++ b/prometheus/metric_test.go @@ -268,7 +268,7 @@ func TestWithExemplarsNativeHistogramMetric(t *testing.T) { } for _, tc := range tcs { - m, err := newDummyConstNativeHistogram(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) + m, err := newNativeHistogramWithClassicBuckets(NewDesc(tc.Name, "None", []string{}, map[string]string{}), tc.Count, tc.Sum, tc.PositiveBuckets, tc.NegativeBuckets, tc.ZeroBucket, tc.NativeHistogramSchema, tc.NativeHistogramZeroThreshold, tc.CreatedTimestamp, tc.Bucket) if err != nil { t.Fail() } @@ -296,7 +296,7 @@ func PointOf[T any](value T) *T { // newNativeHistogramWithClassicBuckets returns a Metric representing // a native histogram that also has classic buckets. This is for testing purposes. -func newDummyConstNativeHistogram( +func newNativeHistogramWithClassicBuckets( desc *Desc, count uint64, sum float64, From 75e3d38005c6a13e2e613dd389da67ba1f4e5991 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Thu, 17 Apr 2025 10:24:39 +0200 Subject: [PATCH 10/13] remove schema check Signed-off-by: Shivanth --- prometheus/metric.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 05fbf084b..b6aa40da6 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -191,9 +191,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { if ((h.ZeroThreshold != nil && *h.ZeroThreshold != 0) || (h.ZeroCount != nil && *h.ZeroCount != 0) || len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0) && e.GetTimestamp() != nil { - if h.Schema != nil { - h.Exemplars = append(h.Exemplars, e) - } + h.Exemplars = append(h.Exemplars, e) if len(h.Bucket) == 0 { // Don't proceed to classic buckets if there are none. continue From e2b5996cd663d99b793b9332dbc06dfa2c292940 Mon Sep 17 00:00:00 2001 From: Shivanth MP Date: Tue, 29 Apr 2025 19:28:06 +0200 Subject: [PATCH 11/13] Update prometheus/metric.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Björn Rabenstein Signed-off-by: Shivanth MP --- prometheus/metric.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index b6aa40da6..28e7f4275 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -188,7 +188,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { case pb.Histogram != nil: h := pb.Histogram for _, e := range m.exemplars { - if ((h.ZeroThreshold != nil && *h.ZeroThreshold != 0) || + if (h.GetZeroThreshold() != 0 || (h.ZeroCount != nil && *h.ZeroCount != 0) || len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0) && e.GetTimestamp() != nil { h.Exemplars = append(h.Exemplars, e) From b8568a3c4618749b038c49ef55a0bcb5150fe7b0 Mon Sep 17 00:00:00 2001 From: Shivanth Date: Fri, 2 May 2025 22:36:03 +0200 Subject: [PATCH 12/13] Use access metho Signed-off-by: Shivanth --- prometheus/metric.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 28e7f4275..70d9fefbe 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -189,7 +189,7 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { h := pb.Histogram for _, e := range m.exemplars { if (h.GetZeroThreshold() != 0 || - (h.ZeroCount != nil && *h.ZeroCount != 0) || len(h.PositiveSpan) != 0 || + h.GetZeroCount() != 0 || len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0) && e.GetTimestamp() != nil { h.Exemplars = append(h.Exemplars, e) if len(h.Bucket) == 0 { From 63eb1ca5ae323cfa9f2cd7bf91f8fa1c8cdc18fa Mon Sep 17 00:00:00 2001 From: Shivanth Date: Fri, 2 May 2025 22:40:19 +0200 Subject: [PATCH 13/13] rearrange Signed-off-by: Shivanth --- prometheus/metric.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prometheus/metric.go b/prometheus/metric.go index 70d9fefbe..76e59f128 100644 --- a/prometheus/metric.go +++ b/prometheus/metric.go @@ -188,9 +188,9 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error { case pb.Histogram != nil: h := pb.Histogram for _, e := range m.exemplars { - if (h.GetZeroThreshold() != 0 || - h.GetZeroCount() != 0 || len(h.PositiveSpan) != 0 || - len(h.NegativeSpan) != 0) && e.GetTimestamp() != nil { + if (h.GetZeroThreshold() != 0 || h.GetZeroCount() != 0 || + len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0) && + e.GetTimestamp() != nil { h.Exemplars = append(h.Exemplars, e) if len(h.Bucket) == 0 { // Don't proceed to classic buckets if there are none.