Skip to content

Commit a7db268

Browse files
perebajkhushijain21
authored andcommitted
feat: add support for MetricTypeSum (open-telemetry#37156)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Prometheus translation rw2 add support for MetricTypeSum. The current work was inspired by open-telemetry#36353 <!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. --> #### Link to tracking issue open-telemetry#33661
1 parent 6eee223 commit a7db268

File tree

5 files changed

+176
-10
lines changed

5 files changed

+176
-10
lines changed
+27
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: add support for metric type sum in FromMetricsV2
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 public function is partially implemented and not 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: [api]

pkg/translator/prometheusremotewrite/helper_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/prometheus/common/model"
1414
"github.com/prometheus/prometheus/model/timestamp"
1515
"github.com/prometheus/prometheus/prompb"
16+
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
1617
"github.com/stretchr/testify/assert"
1718
"github.com/stretchr/testify/require"
1819
"go.opentelemetry.io/collector/pdata/pcommon"
@@ -545,6 +546,46 @@ func Test_getPromExemplars(t *testing.T) {
545546
}
546547
}
547548

549+
func Test_getPromExemplarsV2(t *testing.T) {
550+
tnow := time.Now()
551+
tests := []struct {
552+
name string
553+
histogram pmetric.HistogramDataPoint
554+
expected []writev2.Exemplar
555+
}{
556+
{
557+
name: "with_exemplars_double_value",
558+
histogram: getHistogramDataPointWithExemplars(t, tnow, floatVal1, traceIDValue1, spanIDValue1, label11, value11),
559+
expected: []writev2.Exemplar{
560+
{
561+
Value: floatVal1,
562+
Timestamp: timestamp.FromTime(tnow),
563+
// TODO: after deal with examplar labels on getPromExemplarsV2, add the labels here
564+
// LabelsRefs: []uint32{},
565+
},
566+
},
567+
},
568+
{
569+
name: "with_exemplars_int_value",
570+
histogram: getHistogramDataPointWithExemplars(t, tnow, intVal2, traceIDValue1, spanIDValue1, label11, value11),
571+
expected: []writev2.Exemplar{
572+
{
573+
Value: float64(intVal2),
574+
Timestamp: timestamp.FromTime(tnow),
575+
// TODO: after deal with examplar labels on getPromExemplarsV2, add the labels here
576+
// LabelsRefs: []uint32{},
577+
},
578+
},
579+
},
580+
}
581+
for _, tt := range tests {
582+
t.Run(tt.name, func(t *testing.T) {
583+
requests := getPromExemplarsV2(tt.histogram)
584+
assert.Exactly(t, tt.expected, requests)
585+
})
586+
}
587+
}
588+
548589
func TestAddResourceTargetInfo(t *testing.T) {
549590
resourceAttrMap := map[string]any{
550591
conventions.AttributeServiceName: "service-name",

pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go

+20-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package prometheusremotewrite // import "github.com/open-telemetry/opentelemetry
66
import (
77
"errors"
88
"fmt"
9+
"sort"
910
"strconv"
1011

1112
"github.com/prometheus/prometheus/prompb"
@@ -75,12 +76,19 @@ func (c *prometheusConverterV2) fromMetrics(md pmetric.Metrics, settings Setting
7576
case pmetric.MetricTypeGauge:
7677
dataPoints := metric.Gauge().DataPoints()
7778
if dataPoints.Len() == 0 {
78-
errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name()))
7979
break
8080
}
8181
c.addGaugeNumberDataPoints(dataPoints, resource, settings, promName)
8282
case pmetric.MetricTypeSum:
83-
// TODO implement
83+
dataPoints := metric.Sum().DataPoints()
84+
if dataPoints.Len() == 0 {
85+
break
86+
}
87+
if !metric.Sum().IsMonotonic() {
88+
c.addGaugeNumberDataPoints(dataPoints, resource, settings, promName)
89+
} else {
90+
c.addSumNumberDataPoints(dataPoints, resource, metric, settings, promName)
91+
}
8492
case pmetric.MetricTypeHistogram:
8593
// TODO implement
8694
case pmetric.MetricTypeExponentialHistogram:
@@ -108,13 +116,14 @@ func (c *prometheusConverterV2) timeSeries() []writev2.TimeSeries {
108116
return allTS
109117
}
110118

111-
func (c *prometheusConverterV2) addSample(sample *writev2.Sample, lbls []prompb.Label) *writev2.TimeSeries {
112-
if sample == nil || len(lbls) == 0 {
113-
// This shouldn't happen
114-
return nil
115-
}
116-
119+
func (c *prometheusConverterV2) addSample(sample *writev2.Sample, lbls []prompb.Label) {
117120
buf := make([]uint32, 0, len(lbls)*2)
121+
122+
// TODO: Read the PRW spec to see if labels need to be sorted. If it is, then we need to sort in export code. If not, we can sort in the test. (@dashpole have more context on this)
123+
sort.Slice(lbls, func(i, j int) bool {
124+
return lbls[i].Name < lbls[j].Name
125+
})
126+
118127
var off uint32
119128
for _, l := range lbls {
120129
off = c.symbolTable.Symbolize(l.Name)
@@ -127,6 +136,8 @@ func (c *prometheusConverterV2) addSample(sample *writev2.Sample, lbls []prompb.
127136
Samples: []writev2.Sample{*sample},
128137
}
129138
c.unique[timeSeriesSignature(lbls)] = &ts
139+
}
130140

131-
return &ts
141+
// TODO: implement this function.
142+
func (c *prometheusConverterV2) addTimeSeriesIfNeeded(_ []prompb.Label, _ pcommon.Timestamp, _ pcommon.Timestamp) {
132143
}

pkg/translator/prometheusremotewrite/metrics_to_prw_v2_test.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ func TestFromMetricsV2(t *testing.T) {
3232
{Timestamp: convertTimeStamp(pcommon.Timestamp(ts)), Value: 1.23},
3333
},
3434
},
35+
"1": {
36+
LabelsRefs: []uint32{1, 9, 3, 4, 5, 6, 7, 8},
37+
Samples: []writev2.Sample{
38+
{Timestamp: convertTimeStamp(pcommon.Timestamp(ts)), Value: 1.23},
39+
},
40+
},
3541
}
3642
}
37-
wantedSymbols := []string{"", "series_name_2", "value-2", "series_name_3", "value-3", "__name__", "gauge_1", "series_name_1", "value-1"}
43+
wantedSymbols := []string{"", "series_name_2", "value-2", "series_name_3", "value-3", "__name__", "gauge_1", "series_name_1", "value-1", "sum_1"}
3844
tsMap, symbolsTable, err := FromMetricsV2(payload.Metrics(), settings)
3945
require.NoError(t, err)
4046
require.Equal(t, want(), tsMap)

pkg/translator/prometheusremotewrite/number_data_points_v2.go

+81
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"math"
88

99
"github.com/prometheus/common/model"
10+
"github.com/prometheus/prometheus/model/timestamp"
1011
"github.com/prometheus/prometheus/model/value"
12+
"github.com/prometheus/prometheus/prompb"
1113
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
1214
"go.opentelemetry.io/collector/pdata/pcommon"
1315
"go.opentelemetry.io/collector/pdata/pmetric"
@@ -45,3 +47,82 @@ func (c *prometheusConverterV2) addGaugeNumberDataPoints(dataPoints pmetric.Numb
4547
c.addSample(sample, labels)
4648
}
4749
}
50+
51+
func (c *prometheusConverterV2) addSumNumberDataPoints(dataPoints pmetric.NumberDataPointSlice,
52+
resource pcommon.Resource, metric pmetric.Metric, settings Settings, name string,
53+
) {
54+
for x := 0; x < dataPoints.Len(); x++ {
55+
pt := dataPoints.At(x)
56+
lbls := createAttributes(
57+
resource,
58+
pt.Attributes(),
59+
settings.ExternalLabels,
60+
nil,
61+
true,
62+
model.MetricNameLabel,
63+
name,
64+
)
65+
66+
sample := &writev2.Sample{
67+
// convert ns to ms
68+
Timestamp: convertTimeStamp(pt.Timestamp()),
69+
}
70+
switch pt.ValueType() {
71+
case pmetric.NumberDataPointValueTypeInt:
72+
sample.Value = float64(pt.IntValue())
73+
case pmetric.NumberDataPointValueTypeDouble:
74+
sample.Value = pt.DoubleValue()
75+
}
76+
if pt.Flags().NoRecordedValue() {
77+
sample.Value = math.Float64frombits(value.StaleNaN)
78+
}
79+
// TODO: properly add exemplars to the TimeSeries
80+
c.addSample(sample, lbls)
81+
82+
if settings.ExportCreatedMetric && metric.Sum().IsMonotonic() {
83+
startTimestamp := pt.StartTimestamp()
84+
if startTimestamp != 0 {
85+
return
86+
}
87+
88+
createdLabels := make([]prompb.Label, len(lbls))
89+
copy(createdLabels, lbls)
90+
for i, l := range createdLabels {
91+
if l.Name == model.MetricNameLabel {
92+
createdLabels[i].Value = name + createdSuffix
93+
break
94+
}
95+
}
96+
// TODO: implement this function.
97+
c.addTimeSeriesIfNeeded(createdLabels, startTimestamp, pt.Timestamp())
98+
}
99+
}
100+
}
101+
102+
// getPromExemplarsV2 returns a slice of writev2.Exemplar from pdata exemplars.
103+
func getPromExemplarsV2[T exemplarType](pt T) []writev2.Exemplar {
104+
promExemplars := make([]writev2.Exemplar, 0, pt.Exemplars().Len())
105+
for i := 0; i < pt.Exemplars().Len(); i++ {
106+
exemplar := pt.Exemplars().At(i)
107+
108+
var promExemplar writev2.Exemplar
109+
110+
switch exemplar.ValueType() {
111+
case pmetric.ExemplarValueTypeInt:
112+
promExemplar = writev2.Exemplar{
113+
Value: float64(exemplar.IntValue()),
114+
Timestamp: timestamp.FromTime(exemplar.Timestamp().AsTime()),
115+
}
116+
case pmetric.ExemplarValueTypeDouble:
117+
promExemplar = writev2.Exemplar{
118+
Value: exemplar.DoubleValue(),
119+
Timestamp: timestamp.FromTime(exemplar.Timestamp().AsTime()),
120+
}
121+
}
122+
// TODO append labels to promExemplar.Labels
123+
124+
promExemplars = append(promExemplars, promExemplar)
125+
}
126+
127+
return promExemplars
128+
}

0 commit comments

Comments
 (0)