Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .chloggen/fix-prometheusreceiver-bucketless-nhcb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
change_type: bug_fix

component: receiver/prometheus

note: Prevent the Prometheus receiver from dropping classic histograms without explicit bucket boundaries when `convert_classic_histograms_to_nhcb` is enabled and classic histograms are not retained.

issues: [49893]

change_logs: [user]
6 changes: 0 additions & 6 deletions receiver/prometheusreceiver/internal/metricfamily.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,10 @@ func (mg *metricGroup) toDistributionPoint(dest pmetric.HistogramDataPointSlice)
if mg.isNHCB {
switch {
case mg.hValue != nil:
if len(mg.hValue.CustomValues) == 0 {
Comment thread
dashpole marked this conversation as resolved.
return
}
bounds = make([]float64, len(mg.hValue.CustomValues))
copy(bounds, mg.hValue.CustomValues)
bucketCounts = convertNHCBBDeltBuckets(mg.hValue)
case mg.fhValue != nil:
if len(mg.fhValue.CustomValues) == 0 {
return
}
bounds = make([]float64, len(mg.fhValue.CustomValues))
copy(bounds, mg.fhValue.CustomValues)
bucketCounts = convertNHCBAbsoluteBuckets(mg.fhValue)
Expand Down
84 changes: 84 additions & 0 deletions receiver/prometheusreceiver/internal/metricfamily_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,30 @@ func TestMetricGroupData_toNHCBDistributionUnitTest(t *testing.T) {
return point
},
},
{
name: "integer NHCB without explicit bounds",
metricName: "histogram",
intervalStartTimeMs: 11,
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
integerHistogram: &histogram.Histogram{
Schema: histogram.CustomBucketsSchema,
Count: 42,
Sum: 123.5,
PositiveSpans: []histogram.Span{{Offset: 0, Length: 1}},
PositiveBuckets: []int64{42},
},
want: func() pmetric.HistogramDataPoint {
point := pmetric.NewHistogramDataPoint()
point.SetCount(42)
point.SetSum(123.5)
point.SetTimestamp(pcommon.Timestamp(11 * time.Millisecond))
point.BucketCounts().FromRaw([]uint64{42})
attributes := point.Attributes()
attributes.PutStr("a", "A")
attributes.PutStr("b", "B")
return point
},
},
{
name: "integer NHCB that is stale",
metricName: "histogram",
Expand All @@ -351,6 +375,26 @@ func TestMetricGroupData_toNHCBDistributionUnitTest(t *testing.T) {
return point
},
},
{
name: "integer NHCB without explicit bounds that is stale",
metricName: "histogram",
intervalStartTimeMs: 11,
labels: labels.FromMap(map[string]string{"a": "A", "b": "B"}),
integerHistogram: &histogram.Histogram{
Schema: histogram.CustomBucketsSchema,
Sum: math.Float64frombits(value.StaleNaN),
},
want: func() pmetric.HistogramDataPoint {
point := pmetric.NewHistogramDataPoint()
point.SetTimestamp(pcommon.Timestamp(11 * time.Millisecond))
point.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
point.BucketCounts().FromRaw([]uint64{0})
attributes := point.Attributes()
attributes.PutStr("a", "A")
attributes.PutStr("b", "B")
return point
},
},
{
name: "float NHCB",
metricName: "histogram",
Expand All @@ -376,6 +420,46 @@ func TestMetricGroupData_toNHCBDistributionUnitTest(t *testing.T) {
return point
},
},
{
name: "float NHCB without explicit bounds",
metricName: "histogram",
intervalStartTimeMs: 12,
labels: labels.FromMap(map[string]string{"a": "A"}),
floatHistogram: &histogram.FloatHistogram{
Schema: histogram.CustomBucketsSchema,
Count: 42,
Sum: 123.5,
PositiveSpans: []histogram.Span{{Offset: 0, Length: 1}},
PositiveBuckets: []float64{42},
},
want: func() pmetric.HistogramDataPoint {
point := pmetric.NewHistogramDataPoint()
point.SetCount(42)
point.SetSum(123.5)
point.SetTimestamp(pcommon.Timestamp(12 * time.Millisecond))
point.BucketCounts().FromRaw([]uint64{42})
point.Attributes().PutStr("a", "A")
return point
},
},
{
name: "float NHCB without explicit bounds that is stale",
metricName: "histogram",
intervalStartTimeMs: 12,
labels: labels.FromMap(map[string]string{"a": "A"}),
floatHistogram: &histogram.FloatHistogram{
Schema: histogram.CustomBucketsSchema,
Sum: math.Float64frombits(value.StaleNaN),
},
want: func() pmetric.HistogramDataPoint {
point := pmetric.NewHistogramDataPoint()
point.SetTimestamp(pcommon.Timestamp(12 * time.Millisecond))
point.SetFlags(pmetric.DefaultDataPointFlags.WithNoRecordedValue(true))
point.BucketCounts().FromRaw([]uint64{0})
point.Attributes().PutStr("a", "A")
return point
},
},
{
name: "integer NHCB with negative boundaries",
metricName: "histogram",
Expand Down
66 changes: 66 additions & 0 deletions receiver/prometheusreceiver/metrics_receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,72 @@ func TestCoreMetricsEndToEnd(t *testing.T) {
testComponent(t, targets, nil)
}

func TestBucketlessHistogramNHCBConversion(t *testing.T) {
tests := []struct {
name string
alwaysScrapeClassic bool
expectedHistogramCount int
}{
{
name: "classic histogram not retained",
alwaysScrapeClassic: false,
expectedHistogramCount: 1,
},
{
name: "classic histogram retained",
alwaysScrapeClassic: true,
expectedHistogramCount: 2,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
targets := []*testData{
{
name: "bucketless-histogram",
pages: []mockPrometheusResponse{
{
code: 200,
data: `
# HELP demo_seconds A histogram without explicit buckets.
# TYPE demo_seconds histogram
demo_seconds_sum 123.5
demo_seconds_count 42
`,
},
},
validateFunc: func(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
verifyNumValidScrapeResults(t, td, resourceMetrics)
require.NotEmpty(t, resourceMetrics)
require.Equal(t, expectedScrapeMetricCount+tt.expectedHistogramCount, metricsCount(resourceMetrics[0]))

histogramCount := 0
for _, metric := range getMetrics(resourceMetrics[0]) {
if metric.Name() != "demo_seconds" {
continue
}
require.Equal(t, pmetric.MetricTypeHistogram, metric.Type())
require.Equal(t, 1, metric.Histogram().DataPoints().Len())
compareHistogram(42, 123.5, nil, []uint64{42})(t, metric.Histogram().DataPoints().At(0))
histogramCount++
}
require.Equal(t, tt.expectedHistogramCount, histogramCount)
},
},
}

testComponent(t, targets, nil, func(cfg *PromConfig) {
enabled := true
alwaysScrapeClassic := tt.alwaysScrapeClassic
for _, scrapeConfig := range cfg.ScrapeConfigs {
scrapeConfig.ConvertClassicHistogramsToNHCB = &enabled
scrapeConfig.AlwaysScrapeClassicHistograms = &alwaysScrapeClassic
}
})
})
}
}

// metric type is defined as 'untyped' in the first metric
// and, type hint is missing in the 2nd metric
var untypedMetrics = `
Expand Down
Loading