diff --git a/.chloggen/O11Y-3152.yaml b/.chloggen/O11Y-3152.yaml new file mode 100755 index 000000000000..53e3f2e06c65 --- /dev/null +++ b/.chloggen/O11Y-3152.yaml @@ -0,0 +1,20 @@ +# Use this changelog template to create an entry for release notes. +# If your change doesn't affect end users, such as a test fix or a tooling change, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. + +# 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: prometheusremotewriteexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Emit `otel_scope_info` metric, and add `otel_scope_name` and `otel_scope_version` labels to metrics. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [21091] + +# (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: diff --git a/exporter/prometheusremotewriteexporter/README.md b/exporter/prometheusremotewriteexporter/README.md index ad93925d46f2..8f606962ba85 100644 --- a/exporter/prometheusremotewriteexporter/README.md +++ b/exporter/prometheusremotewriteexporter/README.md @@ -62,6 +62,8 @@ The following settings can be optionally configured: - `enabled` (default = false): If `enabled` is `true`, all the resource attributes will be converted to metric labels by default. - `target_info`: customize `target_info` metric - `enabled` (default = true): If `enabled` is `true`, a `target_info` metric will be generated for each resource metric (see https://github.com/open-telemetry/opentelemetry-specification/pull/2381). +- `scope_info`: customize `otel_scope_info` metric + - `enabled` (default = false): If `enabled` is `true`, a `otel_scope_info` metric will be generated for each instrumentation scope metric (see https://github.com/open-telemetry/opentelemetry-specification/pull/2703). - `export_created_metric`: - `enabled` (default = false): If `enabled` is `true`, a `_created` metric is exported for Summary, Histogram, and Monotonic Sum metric points if diff --git a/exporter/prometheusremotewriteexporter/config.go b/exporter/prometheusremotewriteexporter/config.go index 1f854918d2d5..d15d5bd95c37 100644 --- a/exporter/prometheusremotewriteexporter/config.go +++ b/exporter/prometheusremotewriteexporter/config.go @@ -43,6 +43,9 @@ type Config struct { // TargetInfo allows customizing the target_info metric TargetInfo *TargetInfo `mapstructure:"target_info,omitempty"` + // ScopeInfo allows customizing the scope_info metric + ScopeInfo *ScopeInfo `mapstructure:"scope_info,omitempty"` + // CreatedMetric allows customizing creation of _created metrics CreatedMetric *CreatedMetric `mapstructure:"export_created_metric,omitempty"` @@ -63,6 +66,11 @@ type TargetInfo struct { Enabled bool `mapstructure:"enabled"` } +type ScopeInfo struct { + // Enabled if false the otel_scope_info metric is not generated by the exporter + Enabled bool `mapstructure:"enabled"` +} + // RemoteWriteQueue allows to configure the remote write queue. type RemoteWriteQueue struct { // Enabled if false the queue is not enabled, the export requests diff --git a/exporter/prometheusremotewriteexporter/config_test.go b/exporter/prometheusremotewriteexporter/config_test.go index 19926e845652..97572b5593a0 100644 --- a/exporter/prometheusremotewriteexporter/config_test.go +++ b/exporter/prometheusremotewriteexporter/config_test.go @@ -77,6 +77,9 @@ func TestLoadConfig(t *testing.T) { TargetInfo: &TargetInfo{ Enabled: true, }, + ScopeInfo: &ScopeInfo{ + Enabled: false, + }, CreatedMetric: &CreatedMetric{Enabled: true}, }, }, diff --git a/exporter/prometheusremotewriteexporter/exporter.go b/exporter/prometheusremotewriteexporter/exporter.go index 362efaf5f044..643a10ac636f 100644 --- a/exporter/prometheusremotewriteexporter/exporter.go +++ b/exporter/prometheusremotewriteexporter/exporter.go @@ -75,6 +75,7 @@ func newPRWExporter(cfg *Config, set exporter.CreateSettings) (*prwExporter, err Namespace: cfg.Namespace, ExternalLabels: sanitizedLabels, DisableTargetInfo: !cfg.TargetInfo.Enabled, + DisableScopeInfo: !cfg.ScopeInfo.Enabled, ExportCreatedMetric: cfg.CreatedMetric.Enabled, AddMetricSuffixes: cfg.AddMetricSuffixes, SendMetadata: cfg.SendMetadata, diff --git a/exporter/prometheusremotewriteexporter/exporter_test.go b/exporter/prometheusremotewriteexporter/exporter_test.go index 9bcfcac1a2a0..9f2d1f8782b2 100644 --- a/exporter/prometheusremotewriteexporter/exporter_test.go +++ b/exporter/prometheusremotewriteexporter/exporter_test.go @@ -43,6 +43,9 @@ func Test_NewPRWExporter(t *testing.T) { TargetInfo: &TargetInfo{ Enabled: true, }, + ScopeInfo: &ScopeInfo{ + Enabled: false, + }, CreatedMetric: &CreatedMetric{ Enabled: false, }, @@ -139,6 +142,9 @@ func Test_Start(t *testing.T) { TargetInfo: &TargetInfo{ Enabled: true, }, + ScopeInfo: &ScopeInfo{ + Enabled: false, + }, CreatedMetric: &CreatedMetric{ Enabled: false, }, @@ -696,6 +702,9 @@ func Test_PushMetrics(t *testing.T) { TargetInfo: &TargetInfo{ Enabled: true, }, + ScopeInfo: &ScopeInfo{ + Enabled: false, + }, CreatedMetric: &CreatedMetric{ Enabled: true, }, diff --git a/exporter/prometheusremotewriteexporter/factory.go b/exporter/prometheusremotewriteexporter/factory.go index f90aa41b21ac..080558579080 100644 --- a/exporter/prometheusremotewriteexporter/factory.go +++ b/exporter/prometheusremotewriteexporter/factory.go @@ -99,6 +99,9 @@ func createDefaultConfig() component.Config { TargetInfo: &TargetInfo{ Enabled: true, }, + ScopeInfo: &ScopeInfo{ + Enabled: false, + }, CreatedMetric: &CreatedMetric{ Enabled: false, }, diff --git a/pkg/translator/prometheusremotewrite/helper.go b/pkg/translator/prometheusremotewrite/helper.go index ca5794bda551..5b8179d3a0f7 100644 --- a/pkg/translator/prometheusremotewrite/helper.go +++ b/pkg/translator/prometheusremotewrite/helper.go @@ -44,6 +44,9 @@ const ( spanIDKey = "span_id" infoType = "info" targetMetricName = "target_info" + scopeMetricName = "otel_scope_info" + scopeAttrName = "otel_scope_name" + scopeAttrVersion = "otel_scope_version" ) type bucketBoundsData struct { @@ -155,10 +158,19 @@ func timeSeriesSignature(datatype string, labels *[]prompb.Label) string { // createAttributes creates a slice of Cortex Label with OTLP attributes and pairs of string values. // Unpaired string value is ignored. String pairs overwrites OTLP labels if collision happens, and the overwrite is // logged. Resultant label names are sanitized. -func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externalLabels map[string]string, extras ...string) []prompb.Label { +func createAttributes( + resource pcommon.Resource, + scope pcommon.InstrumentationScope, + attributes pcommon.Map, + externalLabels map[string]string, + extras ...string, +) []prompb.Label { serviceName, haveServiceName := resource.Attributes().Get(conventions.AttributeServiceName) instance, haveInstanceID := resource.Attributes().Get(conventions.AttributeServiceInstanceID) + scopeName := scope.Name() + scopeVersion := scope.Version() + // Calculate the maximum possible number of labels we could return so we can preallocate l maxLabelCount := attributes.Len() + len(externalLabels) + len(extras)/2 @@ -170,6 +182,14 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externa maxLabelCount++ } + if len(scopeName) > 0 { + maxLabelCount++ + } + + if len(scopeVersion) > 0 { + maxLabelCount++ + } + // map ensures no duplicate label name l := make(map[string]string, maxLabelCount) @@ -203,6 +223,13 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externa if haveInstanceID { l[model.InstanceLabel] = instance.AsString() } + // add scope name/version labels + if len(scopeName) > 0 { + l[scopeAttrName] = scopeName + } + if len(scopeVersion) > 0 { + l[scopeAttrVersion] = scopeVersion + } for key, value := range externalLabels { // External labels have already been sanitized if _, alreadyExists := l[key]; alreadyExists { @@ -255,11 +282,18 @@ func isValidAggregationTemporality(metric pmetric.Metric) bool { // addSingleHistogramDataPoint converts pt to 2 + min(len(ExplicitBounds), len(BucketCount)) + 1 samples. It // ignore extra buckets if len(ExplicitBounds) > len(BucketCounts) -func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon.Resource, metric pmetric.Metric, settings Settings, tsMap map[string]*prompb.TimeSeries) { +func addSingleHistogramDataPoint( + pt pmetric.HistogramDataPoint, + resource pcommon.Resource, + scope pcommon.InstrumentationScope, + metric pmetric.Metric, + settings Settings, + tsMap map[string]*prompb.TimeSeries, +) { timestamp := convertTimeStamp(pt.Timestamp()) // sum, count, and buckets of the histogram should append suffix to baseName baseName := prometheustranslator.BuildCompliantName(metric, settings.Namespace, settings.AddMetricSuffixes) - baseLabels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels) + baseLabels := createAttributes(resource, scope, pt.Attributes(), settings.ExternalLabels) createLabels := func(nameSuffix string, extras ...string) []prompb.Label { extraLabelCount := len(extras) / 2 @@ -289,7 +323,6 @@ func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon sumlabels := createLabels(sumStr) addSample(tsMap, sum, sumlabels, metric.Type().String()) - } // treat count as a sample in an individual TimeSeries @@ -454,12 +487,18 @@ func maxTimestamp(a, b pcommon.Timestamp) pcommon.Timestamp { } // addSingleSummaryDataPoint converts pt to len(QuantileValues) + 2 samples. -func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Resource, metric pmetric.Metric, settings Settings, - tsMap map[string]*prompb.TimeSeries) { +func addSingleSummaryDataPoint( + pt pmetric.SummaryDataPoint, + resource pcommon.Resource, + scope pcommon.InstrumentationScope, + metric pmetric.Metric, + settings Settings, + tsMap map[string]*prompb.TimeSeries, +) { timestamp := convertTimeStamp(pt.Timestamp()) // sum and count of the summary should append suffix to baseName baseName := prometheustranslator.BuildCompliantName(metric, settings.Namespace, settings.AddMetricSuffixes) - baseLabels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels) + baseLabels := createAttributes(resource, scope, pt.Attributes(), settings.ExternalLabels) createLabels := func(name string, extras ...string) []prompb.Label { extraLabelCount := len(extras) / 2 @@ -570,7 +609,7 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta if len(settings.Namespace) > 0 { name = settings.Namespace + "_" + name } - labels := createAttributes(resource, attributes, settings.ExternalLabels, nameStr, name) + labels := createAttributes(resource, pcommon.NewInstrumentationScope(), attributes, settings.ExternalLabels, nameStr, name) sample := &prompb.Sample{ Value: float64(1), // convert ns to ms @@ -579,6 +618,33 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta addSample(tsMap, sample, labels, infoType) } +func addScopeInfo( + resource pcommon.Resource, + scope pcommon.InstrumentationScope, + settings Settings, + timestamp pcommon.Timestamp, + tsMap map[string]*prompb.TimeSeries, +) { + if settings.DisableScopeInfo { + return + } + if scope.Attributes().Len() == 0 { + // If the scope doesn't have additional attributes, then otel_scope_info isn't useful. + return + } + + name := scopeMetricName + if len(settings.Namespace) > 0 { + name = settings.Namespace + "_" + name + } + labels := createAttributes(resource, scope, scope.Attributes(), settings.ExternalLabels, nameStr, name) + sample := &prompb.Sample{ + Value: float64(1), + Timestamp: convertTimeStamp(timestamp), + } + addSample(tsMap, sample, labels, infoType) +} + // convertTimeStamp converts OTLP timestamp in ns to timestamp in ms func convertTimeStamp(timestamp pcommon.Timestamp) int64 { return timestamp.AsTime().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) diff --git a/pkg/translator/prometheusremotewrite/helper_test.go b/pkg/translator/prometheusremotewrite/helper_test.go index 0ce9d8dbd043..b630648775cc 100644 --- a/pkg/translator/prometheusremotewrite/helper_test.go +++ b/pkg/translator/prometheusremotewrite/helper_test.go @@ -217,6 +217,7 @@ func Test_createLabelSet(t *testing.T) { tests := []struct { name string resource pcommon.Resource + scope pcommon.InstrumentationScope orig pcommon.Map externalLabels map[string]string extras []string @@ -225,6 +226,7 @@ func Test_createLabelSet(t *testing.T) { { "labels_clean", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{label31, value31, label32, value32}, @@ -238,6 +240,7 @@ func Test_createLabelSet(t *testing.T) { res.Attributes().PutStr("service.instance.id", "127.0.0.1:8080") return res }(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{label31, value31, label32, value32}, @@ -251,14 +254,81 @@ func Test_createLabelSet(t *testing.T) { res.Attributes().PutBool("service.instance.id", true) return res }(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{label31, value31, label32, value32}, getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32, "job", "12345", "instance", "true"), }, + { + "labels_with_scope", + pcommon.NewResource(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.SetName("inst.scope.name") + scope.SetVersion("v1.2.3") + return scope + }(), + lbs1, + map[string]string{}, + []string{label31, value31, label32, value32}, + getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32, "otel_scope_name", "inst.scope.name", "otel_scope_version", "v1.2.3"), + }, + { + "labels_with_scope_name_and_no_version", + pcommon.NewResource(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.SetName("inst.scope.name") + return scope + }(), + lbs1, + map[string]string{}, + []string{label31, value31, label32, value32}, + getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32, "otel_scope_name", "inst.scope.name"), + }, + { + "labels_with_scope_version_and_no_name", + pcommon.NewResource(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.SetVersion("v1.2.3") + return scope + }(), + lbs1, + map[string]string{}, + []string{label31, value31, label32, value32}, + getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32, "otel_scope_version", "v1.2.3"), + }, + { + "labels_with_empty_scope", + pcommon.NewResource(), + pcommon.NewInstrumentationScope(), + lbs1, + map[string]string{}, + []string{label31, value31, label32, value32}, + getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32), + }, + { + "labels_with_scope_attributes", + pcommon.NewResource(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.SetName("inst.scope.name") + scope.Attributes().PutStr("foo", "bar") + scope.Attributes().PutInt("num", 555) + scope.SetVersion("v1.2.3") + return scope + }(), + lbs1, + map[string]string{}, + []string{label31, value31, label32, value32}, + getPromLabels(label11, value11, label12, value12, label31, value31, label32, value32, "otel_scope_name", "inst.scope.name", "otel_scope_version", "v1.2.3"), + }, { "labels_duplicate_in_extras", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{label11, value31}, @@ -267,6 +337,7 @@ func Test_createLabelSet(t *testing.T) { { "labels_dirty", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1Dirty, map[string]string{}, []string{label31 + dirty1, value31, label32, value32}, @@ -275,6 +346,7 @@ func Test_createLabelSet(t *testing.T) { { "no_original_case", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), pcommon.NewMap(), nil, []string{label31, value31, label32, value32}, @@ -283,6 +355,7 @@ func Test_createLabelSet(t *testing.T) { { "empty_extra_case", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{"", ""}, @@ -291,6 +364,7 @@ func Test_createLabelSet(t *testing.T) { { "single_left_over_case", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, map[string]string{}, []string{label31, value31, label32}, @@ -299,6 +373,7 @@ func Test_createLabelSet(t *testing.T) { { "valid_external_labels", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, exlbs1, []string{label31, value31, label32, value32}, @@ -307,6 +382,7 @@ func Test_createLabelSet(t *testing.T) { { "overwritten_external_labels", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs1, exlbs2, []string{label31, value31, label32, value32}, @@ -315,6 +391,7 @@ func Test_createLabelSet(t *testing.T) { { "colliding attributes", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbsColliding, nil, []string{label31, value31, label32, value32}, @@ -323,6 +400,7 @@ func Test_createLabelSet(t *testing.T) { { "sanitize_labels_starts_with_underscore", pcommon.NewResource(), + pcommon.NewInstrumentationScope(), lbs3, exlbs1, []string{label31, value31, label32, value32}, @@ -332,13 +410,14 @@ func Test_createLabelSet(t *testing.T) { // run tests for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - assert.ElementsMatch(t, tt.want, createAttributes(tt.resource, tt.orig, tt.externalLabels, tt.extras...)) + assert.ElementsMatch(t, tt.want, createAttributes(tt.resource, tt.scope, tt.orig, tt.externalLabels, tt.extras...)) }) } } func BenchmarkCreateAttributes(b *testing.B) { r := pcommon.NewResource() + s := pcommon.NewInstrumentationScope() ext := map[string]string{} m := pcommon.NewMap() @@ -350,7 +429,7 @@ func BenchmarkCreateAttributes(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - createAttributes(r, m, ext) + createAttributes(r, s, m, ext) } } @@ -632,6 +711,126 @@ func TestAddResourceTargetInfo(t *testing.T) { } } +func TestAddScopeInfo(t *testing.T) { + for _, tc := range []struct { + desc string + resource pcommon.Resource + scope pcommon.InstrumentationScope + settings Settings + timestamp pcommon.Timestamp + expected map[string]*prompb.TimeSeries + }{ + { + "disable scope info metric", + pcommon.NewResource(), + pcommon.NewInstrumentationScope(), + Settings{DisableScopeInfo: true}, + testdata.TestMetricStartTimestamp, + map[string]*prompb.TimeSeries{}, + }, + { + "empty scope attributes", + pcommon.NewResource(), + pcommon.NewInstrumentationScope(), + Settings{}, + testdata.TestMetricStartTimestamp, + map[string]*prompb.TimeSeries{}, + }, + { + "with scope attributes", + func() pcommon.Resource { + res := pcommon.NewResource() + res.Attributes().PutStr("service.name", "prometheus") + res.Attributes().PutStr("service.instance.id", "127.0.0.1:8080") + res.Attributes().PutStr("res.foo", "res.bar") + return res + }(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.SetName("scope_name_foo") + scope.SetVersion("scope_version_bar") + scope.Attributes().PutStr("foo", "bar") + return scope + }(), + Settings{}, + testdata.TestMetricStartTimestamp, + map[string]*prompb.TimeSeries{ + "info-__name__-otel_scope_info-foo-bar-instance-127.0.0.1:8080-job-prometheus-otel_scope_name-scope_name_foo-otel_scope_version-scope_version_bar": { + Labels: []prompb.Label{ + { + Name: "__name__", + Value: "otel_scope_info", + }, + { + Name: "foo", + Value: "bar", + }, + { + Name: "instance", + Value: "127.0.0.1:8080", + }, + { + Name: "job", + Value: "prometheus", + }, + { + Name: "otel_scope_name", + Value: "scope_name_foo", + }, + { + Name: "otel_scope_version", + Value: "scope_version_bar", + }, + }, + Samples: []prompb.Sample{ + { + Value: 1, + Timestamp: 1581452772000, + }, + }, + }, + }, + }, + { + "with namespace and scope attributes", + pcommon.NewResource(), + func() pcommon.InstrumentationScope { + scope := pcommon.NewInstrumentationScope() + scope.Attributes().PutStr("foo", "bar") + return scope + }(), + Settings{Namespace: "foo"}, + testdata.TestMetricStartTimestamp, + map[string]*prompb.TimeSeries{ + "info-__name__-foo_otel_scope_info-foo-bar": { + Labels: []prompb.Label{ + { + Name: "__name__", + Value: "foo_otel_scope_info", + }, + { + Name: "foo", + Value: "bar", + }, + }, + Samples: []prompb.Sample{ + { + Value: 1, + Timestamp: 1581452772000, + }, + }, + }, + }, + }, + } { + t.Run(tc.desc, func(t *testing.T) { + tsMap := map[string]*prompb.TimeSeries{} + addScopeInfo(tc.resource, tc.scope, tc.settings, tc.timestamp, tsMap) + assert.Exactly(t, tc.expected, tsMap) + }) + } +} + func TestMostRecentTimestampInMetric(t *testing.T) { laterTimestamp := pcommon.NewTimestampFromTime(testdata.TestMetricTime.Add(1 * time.Minute)) metricMultipleTimestamps := testdata.GenerateMetricsOneMetric().ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics().At(0) @@ -757,6 +956,7 @@ func TestAddSingleSummaryDataPoint(t *testing.T) { addSingleSummaryDataPoint( metric.Summary().DataPoints().At(x), pcommon.NewResource(), + pcommon.NewInstrumentationScope(), metric, Settings{ ExportCreatedMetric: true, @@ -868,6 +1068,7 @@ func TestAddSingleHistogramDataPoint(t *testing.T) { addSingleHistogramDataPoint( metric.Histogram().DataPoints().At(x), pcommon.NewResource(), + pcommon.NewInstrumentationScope(), metric, Settings{ ExportCreatedMetric: true, diff --git a/pkg/translator/prometheusremotewrite/histograms.go b/pkg/translator/prometheusremotewrite/histograms.go index bae65448ae00..d1d590344f24 100644 --- a/pkg/translator/prometheusremotewrite/histograms.go +++ b/pkg/translator/prometheusremotewrite/histograms.go @@ -20,11 +20,13 @@ func addSingleExponentialHistogramDataPoint( metric string, pt pmetric.ExponentialHistogramDataPoint, resource pcommon.Resource, + scope pcommon.InstrumentationScope, settings Settings, series map[string]*prompb.TimeSeries, ) error { labels := createAttributes( resource, + scope, pt.Attributes(), settings.ExternalLabels, model.MetricNameLabel, metric, diff --git a/pkg/translator/prometheusremotewrite/histograms_test.go b/pkg/translator/prometheusremotewrite/histograms_test.go index fcef1d4dc924..d23ef5505293 100644 --- a/pkg/translator/prometheusremotewrite/histograms_test.go +++ b/pkg/translator/prometheusremotewrite/histograms_test.go @@ -745,6 +745,7 @@ func TestAddSingleExponentialHistogramDataPoint(t *testing.T) { prometheustranslator.BuildCompliantName(metric, "", true), metric.ExponentialHistogram().DataPoints().At(x), pcommon.NewResource(), + pcommon.NewInstrumentationScope(), Settings{}, gotSeries, ) diff --git a/pkg/translator/prometheusremotewrite/metrics_to_prw.go b/pkg/translator/prometheusremotewrite/metrics_to_prw.go index c189f299d037..deacc1710491 100644 --- a/pkg/translator/prometheusremotewrite/metrics_to_prw.go +++ b/pkg/translator/prometheusremotewrite/metrics_to_prw.go @@ -19,6 +19,7 @@ type Settings struct { Namespace string ExternalLabels map[string]string DisableTargetInfo bool + DisableScopeInfo bool ExportCreatedMetric bool AddMetricSuffixes bool SendMetadata bool @@ -38,6 +39,7 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp var mostRecentTimestamp pcommon.Timestamp for j := 0; j < scopeMetricsSlice.Len(); j++ { scopeMetrics := scopeMetricsSlice.At(j) + scope := scopeMetrics.Scope() metricSlice := scopeMetrics.Metrics() // TODO: decide if instrumentation library information should be exported as labels @@ -59,7 +61,7 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) } for x := 0; x < dataPoints.Len(); x++ { - addSingleGaugeNumberDataPoint(dataPoints.At(x), resource, metric, settings, tsMap) + addSingleGaugeNumberDataPoint(dataPoints.At(x), resource, scope, metric, settings, tsMap) } case pmetric.MetricTypeSum: dataPoints := metric.Sum().DataPoints() @@ -67,7 +69,7 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) } for x := 0; x < dataPoints.Len(); x++ { - addSingleSumNumberDataPoint(dataPoints.At(x), resource, metric, settings, tsMap) + addSingleSumNumberDataPoint(dataPoints.At(x), resource, scope, metric, settings, tsMap) } case pmetric.MetricTypeHistogram: dataPoints := metric.Histogram().DataPoints() @@ -75,7 +77,7 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) } for x := 0; x < dataPoints.Len(); x++ { - addSingleHistogramDataPoint(dataPoints.At(x), resource, metric, settings, tsMap) + addSingleHistogramDataPoint(dataPoints.At(x), resource, scope, metric, settings, tsMap) } case pmetric.MetricTypeExponentialHistogram: dataPoints := metric.ExponentialHistogram().DataPoints() @@ -90,6 +92,7 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp name, dataPoints.At(x), resource, + scope, settings, tsMap, ), @@ -101,12 +104,13 @@ func FromMetrics(md pmetric.Metrics, settings Settings) (tsMap map[string]*promp errs = multierr.Append(errs, fmt.Errorf("empty data points. %s is dropped", metric.Name())) } for x := 0; x < dataPoints.Len(); x++ { - addSingleSummaryDataPoint(dataPoints.At(x), resource, metric, settings, tsMap) + addSingleSummaryDataPoint(dataPoints.At(x), resource, scope, metric, settings, tsMap) } default: errs = multierr.Append(errs, errors.New("unsupported metric type")) } } + addScopeInfo(resource, scope, settings, mostRecentTimestamp, tsMap) } addResourceTargetInfo(resource, settings, mostRecentTimestamp, tsMap) } diff --git a/pkg/translator/prometheusremotewrite/number_data_points.go b/pkg/translator/prometheusremotewrite/number_data_points.go index e4c938d2fa5b..eea633eecd1b 100644 --- a/pkg/translator/prometheusremotewrite/number_data_points.go +++ b/pkg/translator/prometheusremotewrite/number_data_points.go @@ -21,6 +21,7 @@ import ( func addSingleGaugeNumberDataPoint( pt pmetric.NumberDataPoint, resource pcommon.Resource, + scope pcommon.InstrumentationScope, metric pmetric.Metric, settings Settings, series map[string]*prompb.TimeSeries, @@ -28,6 +29,7 @@ func addSingleGaugeNumberDataPoint( name := prometheustranslator.BuildCompliantName(metric, settings.Namespace, settings.AddMetricSuffixes) labels := createAttributes( resource, + scope, pt.Attributes(), settings.ExternalLabels, model.MetricNameLabel, name, @@ -54,6 +56,7 @@ func addSingleGaugeNumberDataPoint( func addSingleSumNumberDataPoint( pt pmetric.NumberDataPoint, resource pcommon.Resource, + scope pcommon.InstrumentationScope, metric pmetric.Metric, settings Settings, series map[string]*prompb.TimeSeries, @@ -61,6 +64,7 @@ func addSingleSumNumberDataPoint( name := prometheustranslator.BuildCompliantName(metric, settings.Namespace, settings.AddMetricSuffixes) labels := createAttributes( resource, + scope, pt.Attributes(), settings.ExternalLabels, model.MetricNameLabel, name, @@ -91,6 +95,7 @@ func addSingleSumNumberDataPoint( if startTimestamp != 0 { createdLabels := createAttributes( resource, + scope, pt.Attributes(), settings.ExternalLabels, nameStr, diff --git a/pkg/translator/prometheusremotewrite/number_data_points_test.go b/pkg/translator/prometheusremotewrite/number_data_points_test.go index 2740d7027fbe..adbe3c937f09 100644 --- a/pkg/translator/prometheusremotewrite/number_data_points_test.go +++ b/pkg/translator/prometheusremotewrite/number_data_points_test.go @@ -57,6 +57,7 @@ func TestAddSingleGaugeNumberDataPoint(t *testing.T) { addSingleGaugeNumberDataPoint( metric.Gauge().DataPoints().At(x), pcommon.NewResource(), + pcommon.NewInstrumentationScope(), metric, Settings{}, gotSeries, @@ -233,6 +234,7 @@ func TestAddSingleSumNumberDataPoint(t *testing.T) { addSingleSumNumberDataPoint( metric.Sum().DataPoints().At(x), pcommon.NewResource(), + pcommon.NewInstrumentationScope(), metric, Settings{ExportCreatedMetric: true}, got,