Skip to content

Commit b3ffb62

Browse files
authored
[chore] prometheusremotewrite.FromMetrics: Fix createLabels closures wrt. extra labels (#31635)
**Description:** <Describe what has changed.> The functions for adding histogram and sum data points have a logic error in their `createLabels` closures. The two `createLabels` closures each take an optional "extras" slice, of label pairs. However, the loop logic doesn't advance to the next pair, just the first label value in fact, so if there were more than one extra pair (there isn't currently), there would be a bug. I'm refactoring the two `createLabels` closures into a free function by the same name, that loops correctly over `extras` arguments and handles if they are of uneven length (ignoring the unpaired `extra`). Also including a comprehensive test suite for `createLabels`. Marking the PR as a chore, since as described above, the logic error should currently not cause a bug. **Link to tracking Issue:** <Issue number if applicable> **Testing:** <Describe what testing was performed and which tests were added.> I've tested locally, there shouldn't be any practical changes since the `createLabels` closures are only called with one extra label pair at most. **Documentation:** <Describe the documentation added.> --------- Signed-off-by: Arve Knudsen <[email protected]>
1 parent 0a12ede commit b3ffb62

File tree

2 files changed

+121
-38
lines changed

2 files changed

+121
-38
lines changed

pkg/translator/prometheusremotewrite/helper.go

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -253,21 +253,6 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
253253
timestamp := convertTimeStamp(pt.Timestamp())
254254
baseLabels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels)
255255

256-
createLabels := func(nameSuffix string, extras ...string) []prompb.Label {
257-
extraLabelCount := len(extras) / 2
258-
labels := make([]prompb.Label, len(baseLabels), len(baseLabels)+extraLabelCount+1) // +1 for name
259-
copy(labels, baseLabels)
260-
261-
for extrasIdx := 0; extrasIdx < extraLabelCount; extrasIdx++ {
262-
labels = append(labels, prompb.Label{Name: extras[extrasIdx], Value: extras[extrasIdx+1]})
263-
}
264-
265-
// sum, count, and buckets of the histogram should append suffix to baseName
266-
labels = append(labels, prompb.Label{Name: model.MetricNameLabel, Value: baseName + nameSuffix})
267-
268-
return labels
269-
}
270-
271256
// If the sum is unset, it indicates the _sum metric point should be
272257
// omitted
273258
if pt.HasSum() {
@@ -280,7 +265,7 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
280265
sum.Value = math.Float64frombits(value.StaleNaN)
281266
}
282267

283-
sumlabels := createLabels(sumStr)
268+
sumlabels := createLabels(baseName+sumStr, baseLabels)
284269
addSample(tsMap, sum, sumlabels, metric.Type().String())
285270

286271
}
@@ -294,7 +279,7 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
294279
count.Value = math.Float64frombits(value.StaleNaN)
295280
}
296281

297-
countlabels := createLabels(countStr)
282+
countlabels := createLabels(baseName+countStr, baseLabels)
298283
addSample(tsMap, count, countlabels, metric.Type().String())
299284

300285
// cumulative count for conversion to cumulative histogram
@@ -316,7 +301,7 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
316301
bucket.Value = math.Float64frombits(value.StaleNaN)
317302
}
318303
boundStr := strconv.FormatFloat(bound, 'f', -1, 64)
319-
labels := createLabels(bucketStr, leStr, boundStr)
304+
labels := createLabels(baseName+bucketStr, baseLabels, leStr, boundStr)
320305
sig := addSample(tsMap, bucket, labels, metric.Type().String())
321306

322307
bucketBounds = append(bucketBounds, bucketBoundsData{sig: sig, bound: bound})
@@ -330,7 +315,7 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
330315
} else {
331316
infBucket.Value = float64(pt.Count())
332317
}
333-
infLabels := createLabels(bucketStr, leStr, pInfStr)
318+
infLabels := createLabels(baseName+bucketStr, baseLabels, leStr, pInfStr)
334319
sig := addSample(tsMap, infBucket, infLabels, metric.Type().String())
335320

336321
bucketBounds = append(bucketBounds, bucketBoundsData{sig: sig, bound: math.Inf(1)})
@@ -339,7 +324,7 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon
339324
// add _created time series if needed
340325
startTimestamp := pt.StartTimestamp()
341326
if settings.ExportCreatedMetric && startTimestamp != 0 {
342-
labels := createLabels(createdSuffix)
327+
labels := createLabels(baseName+createdSuffix, baseLabels)
343328
addCreatedTimeSeriesIfNeeded(tsMap, labels, startTimestamp, pt.Timestamp(), metric.Type().String())
344329
}
345330
}
@@ -452,20 +437,6 @@ func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Res
452437
timestamp := convertTimeStamp(pt.Timestamp())
453438
baseLabels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels)
454439

455-
createLabels := func(name string, extras ...string) []prompb.Label {
456-
extraLabelCount := len(extras) / 2
457-
labels := make([]prompb.Label, len(baseLabels), len(baseLabels)+extraLabelCount+1) // +1 for name
458-
copy(labels, baseLabels)
459-
460-
for extrasIdx := 0; extrasIdx < extraLabelCount; extrasIdx++ {
461-
labels = append(labels, prompb.Label{Name: extras[extrasIdx], Value: extras[extrasIdx+1]})
462-
}
463-
464-
labels = append(labels, prompb.Label{Name: model.MetricNameLabel, Value: name})
465-
466-
return labels
467-
}
468-
469440
// treat sum as a sample in an individual TimeSeries
470441
sum := &prompb.Sample{
471442
Value: pt.Sum(),
@@ -475,7 +446,7 @@ func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Res
475446
sum.Value = math.Float64frombits(value.StaleNaN)
476447
}
477448
// sum and count of the summary should append suffix to baseName
478-
sumlabels := createLabels(baseName + sumStr)
449+
sumlabels := createLabels(baseName+sumStr, baseLabels)
479450
addSample(tsMap, sum, sumlabels, metric.Type().String())
480451

481452
// treat count as a sample in an individual TimeSeries
@@ -486,7 +457,7 @@ func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Res
486457
if pt.Flags().NoRecordedValue() {
487458
count.Value = math.Float64frombits(value.StaleNaN)
488459
}
489-
countlabels := createLabels(baseName + countStr)
460+
countlabels := createLabels(baseName+countStr, baseLabels)
490461
addSample(tsMap, count, countlabels, metric.Type().String())
491462

492463
// process each percentile/quantile
@@ -500,18 +471,36 @@ func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Res
500471
quantile.Value = math.Float64frombits(value.StaleNaN)
501472
}
502473
percentileStr := strconv.FormatFloat(qt.Quantile(), 'f', -1, 64)
503-
qtlabels := createLabels(baseName, quantileStr, percentileStr)
474+
qtlabels := createLabels(baseName, baseLabels, quantileStr, percentileStr)
504475
addSample(tsMap, quantile, qtlabels, metric.Type().String())
505476
}
506477

507478
// add _created time series if needed
508479
startTimestamp := pt.StartTimestamp()
509480
if settings.ExportCreatedMetric && startTimestamp != 0 {
510-
createdLabels := createLabels(baseName + createdSuffix)
481+
createdLabels := createLabels(baseName+createdSuffix, baseLabels)
511482
addCreatedTimeSeriesIfNeeded(tsMap, createdLabels, startTimestamp, pt.Timestamp(), metric.Type().String())
512483
}
513484
}
514485

486+
// createLabels returns a copy of baseLabels, adding to it the pair model.MetricNameLabel=name.
487+
// If extras are provided, corresponding label pairs are also added to the returned slice.
488+
// If extras is uneven length, the last (unpaired) extra will be ignored.
489+
func createLabels(name string, baseLabels []prompb.Label, extras ...string) []prompb.Label {
490+
extraLabelCount := len(extras) / 2
491+
labels := make([]prompb.Label, len(baseLabels), len(baseLabels)+extraLabelCount+1) // +1 for name
492+
copy(labels, baseLabels)
493+
494+
n := len(extras)
495+
n -= n % 2
496+
for extrasIdx := 0; extrasIdx < n; extrasIdx += 2 {
497+
labels = append(labels, prompb.Label{Name: extras[extrasIdx], Value: extras[extrasIdx+1]})
498+
}
499+
500+
labels = append(labels, prompb.Label{Name: model.MetricNameLabel, Value: name})
501+
return labels
502+
}
503+
515504
// addCreatedTimeSeriesIfNeeded adds {name}_created time series with a single
516505
// sample. If the series exists, then new samples won't be added.
517506
func addCreatedTimeSeriesIfNeeded(

pkg/translator/prometheusremotewrite/helper_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,3 +880,97 @@ func TestAddSingleHistogramDataPoint(t *testing.T) {
880880
})
881881
}
882882
}
883+
884+
func TestCreateLabels(t *testing.T) {
885+
testCases := []struct {
886+
name string
887+
metricName string
888+
baseLabels []prompb.Label
889+
extras []string
890+
expected []prompb.Label
891+
}{
892+
{
893+
name: "no base labels, no extras",
894+
metricName: "test",
895+
baseLabels: nil,
896+
expected: []prompb.Label{
897+
{Name: model.MetricNameLabel, Value: "test"},
898+
},
899+
},
900+
{
901+
name: "base labels, no extras",
902+
metricName: "test",
903+
baseLabels: []prompb.Label{
904+
{Name: "base1", Value: "value1"},
905+
{Name: "base2", Value: "value2"},
906+
},
907+
expected: []prompb.Label{
908+
{Name: "base1", Value: "value1"},
909+
{Name: "base2", Value: "value2"},
910+
{Name: model.MetricNameLabel, Value: "test"},
911+
},
912+
},
913+
{
914+
name: "base labels, 1 extra",
915+
metricName: "test",
916+
baseLabels: []prompb.Label{
917+
{Name: "base1", Value: "value1"},
918+
{Name: "base2", Value: "value2"},
919+
},
920+
extras: []string{"extra1", "extraValue1"},
921+
expected: []prompb.Label{
922+
{Name: "base1", Value: "value1"},
923+
{Name: "base2", Value: "value2"},
924+
{Name: "extra1", Value: "extraValue1"},
925+
{Name: model.MetricNameLabel, Value: "test"},
926+
},
927+
},
928+
{
929+
name: "base labels, 2 extras",
930+
metricName: "test",
931+
baseLabels: []prompb.Label{
932+
{Name: "base1", Value: "value1"},
933+
{Name: "base2", Value: "value2"},
934+
},
935+
extras: []string{"extra1", "extraValue1", "extra2", "extraValue2"},
936+
expected: []prompb.Label{
937+
{Name: "base1", Value: "value1"},
938+
{Name: "base2", Value: "value2"},
939+
{Name: "extra1", Value: "extraValue1"},
940+
{Name: "extra2", Value: "extraValue2"},
941+
{Name: model.MetricNameLabel, Value: "test"},
942+
},
943+
},
944+
{
945+
name: "base labels, unpaired extra",
946+
metricName: "test",
947+
baseLabels: []prompb.Label{
948+
{Name: "base1", Value: "value1"},
949+
{Name: "base2", Value: "value2"},
950+
},
951+
extras: []string{"extra1", "extraValue1", "extra2"},
952+
expected: []prompb.Label{
953+
{Name: "base1", Value: "value1"},
954+
{Name: "base2", Value: "value2"},
955+
{Name: "extra1", Value: "extraValue1"},
956+
{Name: model.MetricNameLabel, Value: "test"},
957+
},
958+
},
959+
{
960+
name: "no base labels, 1 extra",
961+
metricName: "test",
962+
baseLabels: nil,
963+
extras: []string{"extra1", "extraValue1"},
964+
expected: []prompb.Label{
965+
{Name: "extra1", Value: "extraValue1"},
966+
{Name: model.MetricNameLabel, Value: "test"},
967+
},
968+
},
969+
}
970+
for _, tc := range testCases {
971+
t.Run(tc.name, func(t *testing.T) {
972+
lbls := createLabels(tc.metricName, tc.baseLabels, tc.extras...)
973+
assert.Equal(t, lbls, tc.expected)
974+
})
975+
}
976+
}

0 commit comments

Comments
 (0)