diff --git a/.chloggen/add-metrictypesum-support.yaml b/.chloggen/add-metrictypesum-support.yaml new file mode 100644 index 000000000000..639bdcbf93ef --- /dev/null +++ b/.chloggen/add-metrictypesum-support.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/translator/prometheusremotewrite + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add support for metric type sum in FromMetricsV2 + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [33661] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: The public function is partially implemented and not ready for use + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [api] diff --git a/pkg/translator/prometheusremotewrite/helper_test.go b/pkg/translator/prometheusremotewrite/helper_test.go index 631ff7c831b1..a299d2d179f7 100644 --- a/pkg/translator/prometheusremotewrite/helper_test.go +++ b/pkg/translator/prometheusremotewrite/helper_test.go @@ -13,6 +13,7 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/prompb" + writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" @@ -537,6 +538,46 @@ func Test_getPromExemplars(t *testing.T) { } } +func Test_getPromExemplarsV2(t *testing.T) { + tnow := time.Now() + tests := []struct { + name string + histogram pmetric.HistogramDataPoint + expected []writev2.Exemplar + }{ + { + name: "with_exemplars_double_value", + histogram: getHistogramDataPointWithExemplars(t, tnow, floatVal1, traceIDValue1, spanIDValue1, label11, value11), + expected: []writev2.Exemplar{ + { + Value: floatVal1, + Timestamp: timestamp.FromTime(tnow), + // TODO: after deal with examplar labels on getPromExemplarsV2, add the labels here + // LabelsRefs: []uint32{}, + }, + }, + }, + { + name: "with_exemplars_int_value", + histogram: getHistogramDataPointWithExemplars(t, tnow, intVal2, traceIDValue1, spanIDValue1, label11, value11), + expected: []writev2.Exemplar{ + { + Value: float64(intVal2), + Timestamp: timestamp.FromTime(tnow), + // TODO: after deal with examplar labels on getPromExemplarsV2, add the labels here + // LabelsRefs: []uint32{}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + requests := getPromExemplarsV2(tt.histogram) + assert.Exactly(t, tt.expected, requests) + }) + } +} + func TestAddResourceTargetInfo(t *testing.T) { resourceAttrMap := map[string]any{ conventions.AttributeServiceName: "service-name", diff --git a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go index 4fde69183f2f..a69c9309f07c 100644 --- a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go +++ b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go @@ -75,12 +75,19 @@ func (c *prometheusConverterV2) fromMetrics(md pmetric.Metrics, settings Setting case pmetric.MetricTypeGauge: dataPoints := metric.Gauge().DataPoints() if dataPoints.Len() == 0 { - errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) break } c.addGaugeNumberDataPoints(dataPoints, resource, settings, promName) case pmetric.MetricTypeSum: - // TODO implement + dataPoints := metric.Sum().DataPoints() + if dataPoints.Len() == 0 { + break + } + if !metric.Sum().IsMonotonic() { + c.addGaugeNumberDataPoints(dataPoints, resource, settings, promName) + } else { + c.addSumNumberDataPoints(dataPoints, resource, metric, settings, promName) + } case pmetric.MetricTypeHistogram: // TODO implement case pmetric.MetricTypeExponentialHistogram: @@ -130,3 +137,7 @@ func (c *prometheusConverterV2) addSample(sample *writev2.Sample, lbls []prompb. return &ts } + +// TODO: implement this function. +func (c *prometheusConverterV2) addTimeSeriesIfNeeded(_ []prompb.Label, _ pcommon.Timestamp, _ pcommon.Timestamp) { +} diff --git a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2_test.go b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2_test.go index ee56b78428e4..198db9825b81 100644 --- a/pkg/translator/prometheusremotewrite/metrics_to_prw_v2_test.go +++ b/pkg/translator/prometheusremotewrite/metrics_to_prw_v2_test.go @@ -32,9 +32,15 @@ func TestFromMetricsV2(t *testing.T) { {Timestamp: convertTimeStamp(pcommon.Timestamp(ts)), Value: 1.23}, }, }, + "1": { + LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 9}, + Samples: []writev2.Sample{ + {Timestamp: convertTimeStamp(pcommon.Timestamp(ts)), Value: 1.23}, + }, + }, } } - wantedSymbols := []string{"", "series_name_2", "value-2", "series_name_3", "value-3", "__name__", "gauge_1", "series_name_1", "value-1"} + wantedSymbols := []string{"", "series_name_2", "value-2", "series_name_3", "value-3", "__name__", "gauge_1", "series_name_1", "value-1", "sum_1"} tsMap, symbolsTable, err := FromMetricsV2(payload.Metrics(), settings) require.NoError(t, err) require.Equal(t, want(), tsMap) diff --git a/pkg/translator/prometheusremotewrite/number_data_points_v2.go b/pkg/translator/prometheusremotewrite/number_data_points_v2.go index 3509f7a95b97..a264e27c7c89 100644 --- a/pkg/translator/prometheusremotewrite/number_data_points_v2.go +++ b/pkg/translator/prometheusremotewrite/number_data_points_v2.go @@ -7,7 +7,9 @@ import ( "math" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/model/value" + "github.com/prometheus/prometheus/prompb" writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" @@ -45,3 +47,82 @@ func (c *prometheusConverterV2) addGaugeNumberDataPoints(dataPoints pmetric.Numb c.addSample(sample, labels) } } + +func (c *prometheusConverterV2) addSumNumberDataPoints(dataPoints pmetric.NumberDataPointSlice, + resource pcommon.Resource, metric pmetric.Metric, settings Settings, name string, +) { + for x := 0; x < dataPoints.Len(); x++ { + pt := dataPoints.At(x) + lbls := createAttributes( + resource, + pt.Attributes(), + settings.ExternalLabels, + nil, + true, + model.MetricNameLabel, + name, + ) + + sample := &writev2.Sample{ + // convert ns to ms + Timestamp: convertTimeStamp(pt.Timestamp()), + } + switch pt.ValueType() { + case pmetric.NumberDataPointValueTypeInt: + sample.Value = float64(pt.IntValue()) + case pmetric.NumberDataPointValueTypeDouble: + sample.Value = pt.DoubleValue() + } + if pt.Flags().NoRecordedValue() { + sample.Value = math.Float64frombits(value.StaleNaN) + } + // TODO: properly add exemplars to the TimeSeries + c.addSample(sample, lbls) + + if settings.ExportCreatedMetric && metric.Sum().IsMonotonic() { + startTimestamp := pt.StartTimestamp() + if startTimestamp != 0 { + return + } + + createdLabels := make([]prompb.Label, len(lbls)) + copy(createdLabels, lbls) + for i, l := range createdLabels { + if l.Name == model.MetricNameLabel { + createdLabels[i].Value = name + createdSuffix + break + } + } + // TODO: implement this function. + c.addTimeSeriesIfNeeded(createdLabels, startTimestamp, pt.Timestamp()) + } + } +} + +// getPromExemplarsV2 returns a slice of writev2.Exemplar from pdata exemplars. +func getPromExemplarsV2[T exemplarType](pt T) []writev2.Exemplar { + promExemplars := make([]writev2.Exemplar, 0, pt.Exemplars().Len()) + for i := 0; i < pt.Exemplars().Len(); i++ { + exemplar := pt.Exemplars().At(i) + + var promExemplar writev2.Exemplar + + switch exemplar.ValueType() { + case pmetric.ExemplarValueTypeInt: + promExemplar = writev2.Exemplar{ + Value: float64(exemplar.IntValue()), + Timestamp: timestamp.FromTime(exemplar.Timestamp().AsTime()), + } + case pmetric.ExemplarValueTypeDouble: + promExemplar = writev2.Exemplar{ + Value: exemplar.DoubleValue(), + Timestamp: timestamp.FromTime(exemplar.Timestamp().AsTime()), + } + } + // TODO append labels to promExemplar.Labels + + promExemplars = append(promExemplars, promExemplar) + } + + return promExemplars +}