Skip to content

Commit 02ac867

Browse files
authored
[chore] prom translation rw2 add support for histogram (#40494)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Adds support for adding histograms in the RW2 translation path. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes Partially implements #33661 (when merging PR please don't close the tracing issue) <!--Describe what testing was performed and which tests were added.--> #### Testing * [x] Added unit test * [x] e2e ran with prometheus Histogram metrics in prometheus ui ![image](https://github.com/user-attachments/assets/6da34adf-b17e-4dc5-a132-5423d9df835e)
1 parent a6b0f06 commit 02ac867

File tree

4 files changed

+188
-1
lines changed

4 files changed

+188
-1
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: pkg/translator/prometheusremotewrite
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "`FromMetricsV2` now supports translating histograms."
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [33661]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: The translation layer for Prometheus remote write 2 now supports histograms but is not fully implemented and ready for use.
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

pkg/translator/prometheusremotewrite/helper_v2.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,38 @@ func (c *prometheusConverterV2) addSummaryDataPoints(dataPoints pmetric.SummaryD
112112
}
113113
}
114114
}
115+
116+
func (c *prometheusConverterV2) addHistogramDataPoints(dataPoints pmetric.HistogramDataPointSlice,
117+
resource pcommon.Resource, settings Settings, baseName string, metadata metadata,
118+
) {
119+
for x := 0; x < dataPoints.Len(); x++ {
120+
pt := dataPoints.At(x)
121+
timestamp := convertTimeStamp(pt.Timestamp())
122+
baseLabels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels, nil, false)
123+
noRecordedValue := pt.Flags().NoRecordedValue()
124+
125+
// If the sum is unset, it indicates the _sum metric point should be
126+
// omitted
127+
if pt.HasSum() {
128+
c.addSampleWithLabels(pt.Sum(), timestamp, noRecordedValue, baseName+sumStr, baseLabels, "", "", metadata)
129+
}
130+
131+
// treat count as a sample in an individual TimeSeries
132+
c.addSampleWithLabels(float64(pt.Count()), timestamp, noRecordedValue, baseName+countStr, baseLabels, "", "", metadata)
133+
134+
// cumulative count for conversion to cumulative histogram
135+
var cumulativeCount uint64
136+
137+
// process each bound, based on histograms proto definition, # of buckets = # of explicit bounds + 1
138+
for i := 0; i < pt.ExplicitBounds().Len() && i < pt.BucketCounts().Len(); i++ {
139+
bound := pt.ExplicitBounds().At(i)
140+
cumulativeCount += pt.BucketCounts().At(i)
141+
boundStr := strconv.FormatFloat(bound, 'f', -1, 64)
142+
c.addSampleWithLabels(float64(cumulativeCount), timestamp, noRecordedValue, baseName+bucketStr, baseLabels, leStr, boundStr, metadata)
143+
}
144+
// add le=+Inf bucket
145+
c.addSampleWithLabels(float64(pt.Count()), timestamp, noRecordedValue, baseName+bucketStr, baseLabels, leStr, pInfStr, metadata)
146+
147+
// TODO implement exemplars support
148+
}
149+
}

pkg/translator/prometheusremotewrite/helper_v2_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,124 @@ func TestPrometheusConverterV2_AddSummaryDataPoints(t *testing.T) {
277277
})
278278
}
279279
}
280+
281+
func TestPrometheusConverterV2_AddHistogramDataPoints(t *testing.T) {
282+
ts := pcommon.Timestamp(time.Now().UnixNano())
283+
tests := []struct {
284+
name string
285+
metric func() pmetric.Metric
286+
want func() map[uint64]*writev2.TimeSeries
287+
}{
288+
{
289+
name: "histogram with start time",
290+
metric: func() pmetric.Metric {
291+
metric := pmetric.NewMetric()
292+
metric.SetName("test_hist")
293+
metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
294+
295+
pt := metric.Histogram().DataPoints().AppendEmpty()
296+
pt.SetTimestamp(ts)
297+
pt.SetStartTimestamp(ts)
298+
299+
return metric
300+
},
301+
want: func() map[uint64]*writev2.TimeSeries {
302+
labels := []prompb.Label{
303+
{Name: model.MetricNameLabel, Value: "test_hist" + countStr},
304+
}
305+
infLabels := []prompb.Label{
306+
{Name: model.MetricNameLabel, Value: "test_hist_bucket"},
307+
{Name: model.BucketLabel, Value: "+Inf"},
308+
}
309+
return map[uint64]*writev2.TimeSeries{
310+
timeSeriesSignature(infLabels): {
311+
LabelsRefs: []uint32{1, 3, 4, 5},
312+
Samples: []writev2.Sample{
313+
{Value: 0, Timestamp: convertTimeStamp(ts)},
314+
},
315+
Metadata: writev2.Metadata{
316+
Type: writev2.Metadata_METRIC_TYPE_HISTOGRAM,
317+
HelpRef: 0,
318+
},
319+
},
320+
timeSeriesSignature(labels): {
321+
LabelsRefs: []uint32{1, 2},
322+
Samples: []writev2.Sample{
323+
{Value: 0, Timestamp: convertTimeStamp(ts)},
324+
},
325+
Metadata: writev2.Metadata{
326+
Type: writev2.Metadata_METRIC_TYPE_HISTOGRAM,
327+
HelpRef: 0,
328+
},
329+
},
330+
}
331+
},
332+
},
333+
{
334+
name: "histogram without start time",
335+
metric: func() pmetric.Metric {
336+
metric := pmetric.NewMetric()
337+
metric.SetName("test_hist")
338+
metric.SetEmptyHistogram().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
339+
340+
pt := metric.Histogram().DataPoints().AppendEmpty()
341+
pt.SetTimestamp(ts)
342+
343+
return metric
344+
},
345+
want: func() map[uint64]*writev2.TimeSeries {
346+
labels := []prompb.Label{
347+
{Name: model.MetricNameLabel, Value: "test_hist" + countStr},
348+
}
349+
infLabels := []prompb.Label{
350+
{Name: model.MetricNameLabel, Value: "test_hist_bucket"},
351+
{Name: model.BucketLabel, Value: "+Inf"},
352+
}
353+
return map[uint64]*writev2.TimeSeries{
354+
timeSeriesSignature(infLabels): {
355+
LabelsRefs: []uint32{1, 3, 4, 5},
356+
Samples: []writev2.Sample{
357+
{Value: 0, Timestamp: convertTimeStamp(ts)},
358+
},
359+
Metadata: writev2.Metadata{
360+
Type: writev2.Metadata_METRIC_TYPE_HISTOGRAM,
361+
HelpRef: 0,
362+
},
363+
},
364+
timeSeriesSignature(labels): {
365+
LabelsRefs: []uint32{1, 2},
366+
Samples: []writev2.Sample{
367+
{Value: 0, Timestamp: convertTimeStamp(ts)},
368+
},
369+
Metadata: writev2.Metadata{
370+
Type: writev2.Metadata_METRIC_TYPE_HISTOGRAM,
371+
HelpRef: 0,
372+
},
373+
},
374+
}
375+
},
376+
},
377+
}
378+
for _, tt := range tests {
379+
t.Run(tt.name, func(t *testing.T) {
380+
metric := tt.metric()
381+
converter := newPrometheusConverterV2()
382+
m := metadata{
383+
Type: otelMetricTypeToPromMetricTypeV2(metric),
384+
Help: metric.Description(),
385+
Unit: prometheustranslator.BuildCompliantPrometheusUnit(metric.Unit()),
386+
}
387+
converter.addHistogramDataPoints(
388+
metric.Histogram().DataPoints(),
389+
pcommon.NewResource(),
390+
Settings{},
391+
metric.Name(),
392+
m,
393+
)
394+
395+
assert.Equal(t, tt.want(), converter.unique)
396+
// TODO check when conflicts handling is implemented
397+
// assert.Empty(t, converter.conflicts)
398+
})
399+
}
400+
}

pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ func (c *prometheusConverterV2) fromMetrics(md pmetric.Metrics, settings Setting
101101
c.addSumNumberDataPoints(dataPoints, resource, metric, settings, promName, m)
102102
}
103103
case pmetric.MetricTypeHistogram:
104-
// TODO implement
104+
dataPoints := metric.Histogram().DataPoints()
105+
if dataPoints.Len() == 0 {
106+
break
107+
}
108+
c.addHistogramDataPoints(dataPoints, resource, settings, promName, m)
105109
case pmetric.MetricTypeExponentialHistogram:
106110
// TODO implement
107111
case pmetric.MetricTypeSummary:

0 commit comments

Comments
 (0)