diff --git a/CHANGELOG.md b/CHANGELOG.md index 221de9c5a17..ba46bba4743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed - Use `context.Background()` as default context instead of nil in `go.opentelemetry.io/contrib/bridges/otellogr`. (#6527) +- Convert Prometheus histogram buckets to non-cumulative otel histogram buckets in `go.opentelemetry.io/contrib/bridges/prometheus`. (#6685) - Don't start spans that never end for filtered out gRPC stats handler in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#6695) diff --git a/bridges/prometheus/producer.go b/bridges/prometheus/producer.go index e94d07a6d46..37475c3d6df 100644 --- a/bridges/prometheus/producer.go +++ b/bridges/prometheus/producer.go @@ -296,6 +296,7 @@ func convertBuckets(buckets []*dto.Bucket, sampleCount uint64) ([]float64, []uin bucketCounts = make([]uint64, len(buckets)+1) } exemplars := make([]metricdata.Exemplar[float64], 0) + var previousCount uint64 for i, bucket := range buckets { // The last bound may be the +Inf bucket, which is implied in OTel, but // is explicit in Prometheus. Skip the last boundary if it is the +Inf @@ -303,7 +304,7 @@ func convertBuckets(buckets []*dto.Bucket, sampleCount uint64) ([]float64, []uin if bound := bucket.GetUpperBound(); !math.IsInf(bound, +1) { bounds[i] = bound } - bucketCounts[i] = bucket.GetCumulativeCount() + previousCount, bucketCounts[i] = bucket.GetCumulativeCount(), bucket.GetCumulativeCount()-previousCount if ex := bucket.GetExemplar(); ex != nil { exemplars = append(exemplars, convertExemplar(ex)) } @@ -311,7 +312,7 @@ func convertBuckets(buckets []*dto.Bucket, sampleCount uint64) ([]float64, []uin if !hasInf { // The Inf bucket was missing, so set the last bucket counts to the // overall count - bucketCounts[len(bucketCounts)-1] = sampleCount + bucketCounts[len(bucketCounts)-1] = sampleCount - previousCount } return bounds, bucketCounts, exemplars } diff --git a/bridges/prometheus/producer_test.go b/bridges/prometheus/producer_test.go index 801fe27dd23..1b578201ccb 100644 --- a/bridges/prometheus/producer_test.go +++ b/bridges/prometheus/producer_test.go @@ -229,6 +229,44 @@ func TestProduce(t *testing.T) { }, }}, }, + { + name: "histogram cumulative values to non-cumulative", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "test_histogram_metric", + Help: "A histogram metric for testing", + ConstLabels: prometheus.Labels(map[string]string{ + "foo": "bar", + }), + }) + reg.MustRegister(metric) + metric.Observe(0.01) + }, + expected: []metricdata.ScopeMetrics{{ + Scope: instrumentation.Scope{ + Name: scopeName, + }, + Metrics: []metricdata.Metrics{ + { + Name: "test_histogram_metric", + Description: "A histogram metric for testing", + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + Count: 1, + Sum: 0.01, + Bounds: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + BucketCounts: []uint64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Attributes: attribute.NewSet(attribute.String("foo", "bar")), + Exemplars: []metricdata.Exemplar[float64]{}, + }, + }, + }, + }, + }, + }}, + }, { name: "histogram with exemplar", testFn: func(reg *prometheus.Registry) {