Repository: GoogleCloudPlatform/opentelemetry-operations-go
Pull request #571 (2023-01-17 20:03:02)

Add otel_scope_info metric to GMP exporter
Fixes https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/issues/543

Following the spec at https://github.com/open-telemetry/opentelemetry-specification/blob/v1.16.0/specification/compatibility/prometheus_and_openmetrics.md#instrumentation-scope-1, this adds an `otel_scope_info` metric:

* `otel_scope_name` and `otel_scope_version` are populated if present on the scope
* additional scope attributes are added as labels
* this is broken into its own function so that it can be optionally configurable through the exporter

codecov[bot]: # [Codecov](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/pull/571?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) Report
> Merging [#571](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/pull/571?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (88c7ead) into [main](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/commit/f1a3ebdeaef3e6f2db758b2145aba58577e0474a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) (f1a3ebd) will **increase** coverage by `0.13%`.
> The diff coverage is `86.66%`.

```diff
@@            Coverage Diff             @@
##             main     #571      +/-   ##
==========================================
+ Coverage   64.16%   64.29%   +0.13%     
==========================================
  Files          38       38              
  Lines        4216     4243      +27     
==========================================
+ Hits         2705     2728      +23     
- Misses       1398     1401       +3     
- Partials      113      114       +1     
```


| [Impacted Files](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/pull/571?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform) | Coverage Δ | |
|---|---|---|
| [...collector/googlemanagedprometheus/extra\_metrics.go](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/pull/571?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-ZXhwb3J0ZXIvY29sbGVjdG9yL2dvb2dsZW1hbmFnZWRwcm9tZXRoZXVzL2V4dHJhX21ldHJpY3MuZ28=) | `92.85% <84.00%> (-7.15%)` | :arrow_down: |
| [...tor/integrationtest/testcases/testcases\_metrics.go](https://codecov.io/gh/GoogleCloudPlatform/opentelemetry-operations-go/pull/571?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform#diff-ZXhwb3J0ZXIvY29sbGVjdG9yL2ludGVncmF0aW9udGVzdC90ZXN0Y2FzZXMvdGVzdGNhc2VzX21ldHJpY3MuZ28=) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=GoogleCloudPlatform)


Patch:
diff --git a/exporter/collector/config.go b/exporter/collector/config.go
index d438927..fbda5ed 100644
--- a/exporter/collector/config.go
+++ b/exporter/collector/config.go
@@ -159,6 +159,10 @@ type LogConfig struct {
 	// Defaults to empty, which won't include any additional resource labels.
 	ResourceFilters []ResourceFilter `mapstructure:"resource_filters"`
 	ClientConfig    ClientConfig     `mapstructure:",squash"`
+	// ServiceResourceLabels, if true, causes the exporter to copy OTel's
+	// service.name, service.namespace, and service.instance.id resource attributes into the Cloud Logging LogEntry labels.
+	// Disabling this option does not prevent resource_filters from adding those labels. Default is true.
+	ServiceResourceLabels bool `mapstructure:"service_resource_labels"`
 }
 
 // Known metric domains. Note: This is now configurable for advanced usages.
@@ -168,6 +172,9 @@ var domains = []string{"googleapis.com", "kubernetes.io", "istio.io", "knative.d
 func DefaultConfig() Config {
 	return Config{
 		UserAgent: "opentelemetry-collector-contrib {{version}}",
+		LogConfig: LogConfig{
+			ServiceResourceLabels: true,
+		},
 		MetricConfig: MetricConfig{
 			KnownDomains:                     domains,
 			Prefix:                           "workload.googleapis.com",
diff --git a/exporter/collector/googlemanagedprometheus/extra_metrics.go b/exporter/collector/googlemanagedprometheus/extra_metrics.go
index 35ef3d2..32f531a 100644
--- a/exporter/collector/googlemanagedprometheus/extra_metrics.go
+++ b/exporter/collector/googlemanagedprometheus/extra_metrics.go
@@ -19,10 +19,11 @@ import (
 	"go.opentelemetry.io/collector/pdata/pmetric"
 )
 
-// AddTargetInfo extracts the target_info metric from each ResourceMetric associated with the input pmetric.Metrics
+// AddTargetInfoMetric inserts target_info for each resource.
+// First, it extracts the target_info metric from each ResourceMetric associated with the input pmetric.Metrics
 // and inserts it into each ScopeMetric for that resource, as specified in
 // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.16.0/specification/compatibility/prometheus_and_openmetrics.md#resource-attributes-1
-func AddTargetInfo(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
+func AddTargetInfoMetric(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
 	// initialize a new ResourceMetricSlice, which will hold our target_info metrics for each RM/Scope
 	resourceMetricSlice := pmetric.NewResourceMetricsSlice()
 	rms := m.ResourceMetrics()
@@ -51,6 +52,35 @@ func AddTargetInfo(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
 	return resourceMetricSlice
 }
 
+// AddScopeInfoMetric adds the otel_scope_info metric to a Metrics slice as specified in
+// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.16.0/specification/compatibility/prometheus_and_openmetrics.md#instrumentation-scope-1
+func AddScopeInfoMetric(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
+	resourceMetricSlice := pmetric.NewResourceMetricsSlice()
+	rms := m.ResourceMetrics()
+	for i := 0; i < rms.Len(); i++ {
+		resourceMetricSlice.AppendEmpty()
+
+		sms := rms.At(i).ScopeMetrics()
+		for j := 0; j < sms.Len(); j++ {
+			sm := sms.At(j)
+
+			scopeInfoMetric := resourceMetricSlice.At(i).ScopeMetrics().AppendEmpty().Metrics().AppendEmpty()
+			scopeInfoMetric.SetName("otel_scope_info")
+
+			dataPoint := scopeInfoMetric.SetEmptyGauge().DataPoints().AppendEmpty()
+			dataPoint.SetIntValue(1)
+
+			dataPoint.Attributes().PutStr("otel_scope_name", sm.Scope().Name())
+			dataPoint.Attributes().PutStr("otel_scope_version", sm.Scope().Version())
+			sm.Scope().Attributes().Range(func(k string, v pcommon.Value) bool {
+				dataPoint.Attributes().PutStr(k, v.AsString())
+				return true
+			})
+		}
+	}
+	return resourceMetricSlice
+}
+
 func isSpecialAttribute(attributeKey string) bool {
 	for _, keys := range promTargetKeys {
 		for _, specialKey := range keys {
diff --git a/exporter/collector/logs.go b/exporter/collector/logs.go
index 323677f..7a8849a 100644
--- a/exporter/collector/logs.go
+++ b/exporter/collector/logs.go
@@ -212,7 +212,7 @@ func (l logMapper) createEntries(ld plog.Logs) ([]*logpb.LogEntry, error) {
 	for i := 0; i < ld.ResourceLogs().Len(); i++ {
 		rl := ld.ResourceLogs().At(i)
 		mr := defaultResourceToMonitoredResource(rl.Resource())
-		extraResourceLabels := resourceToLabels(rl.Resource(), false, l.cfg.LogConfig.ResourceFilters, l.obs.log)
+		extraResourceLabels := resourceToLabels(rl.Resource(), l.cfg.LogConfig.ServiceResourceLabels, l.cfg.LogConfig.ResourceFilters, l.obs.log)
 		projectID := l.cfg.ProjectID
 		// override project ID with gcp.project.id, if present
 		if projectFromResource, found := rl.Resource().Attributes().Get(resourcemapping.ProjectIDAttributeKey); found {
@@ -224,6 +224,12 @@ func (l logMapper) createEntries(ld plog.Logs) ([]*logpb.LogEntry, error) {
 			logLabels := mergeLogLabels(sl.Scope().Name(), sl.Scope().Version(), extraResourceLabels)
 
 			for k := 0; k < sl.LogRecords().Len(); k++ {
+				// make a copy of logLabels so that shared attributes (scope/resource) are copied across LogRecords,
+				// but that individual LogRecord attributes don't copy over to other Records via map reference.
+				entryLabels := make(map[string]string)
+				for k, v := range logLabels {
+					entryLabels[k] = v
+				}
 				log := sl.LogRecords().At(k)
 
 				// We can't just set logName on these entries otherwise the conversion to internal will fail
@@ -238,7 +244,7 @@ func (l logMapper) createEntries(ld plog.Logs) ([]*logpb.LogEntry, error) {
 				splitEntries, err := l.logToSplitEntries(
 					log,
 					mr,
-					logLabels,
+					entryLabels,
 					time.Now(),
 					logName,
 					projectID,


Test patch:
diff --git a/exporter/collector/googlemanagedprometheus/extra_metrics_test.go b/exporter/collector/googlemanagedprometheus/extra_metrics_test.go
index 1aeb89e..c19fdd9 100644
--- a/exporter/collector/googlemanagedprometheus/extra_metrics_test.go
+++ b/exporter/collector/googlemanagedprometheus/extra_metrics_test.go
@@ -21,14 +21,16 @@ import (
 	"go.opentelemetry.io/collector/pdata/pmetric"
 )
 
-func TestAddTargetInfo(t *testing.T) {
+func TestAddExtraMetrics(t *testing.T) {
 	for _, tc := range []struct {
+		testFunc func(pmetric.Metrics) pmetric.ResourceMetricsSlice
 		input    pmetric.Metrics
 		expected pmetric.ResourceMetricsSlice
 		name     string
 	}{
 		{
-			name: "add target info from resource metric",
+			name:     "add target info from resource metric",
+			testFunc: AddTargetInfoMetric,
 			input: func() pmetric.Metrics {
 				metrics := pmetric.NewMetrics()
 				rm := metrics.ResourceMetrics().AppendEmpty()
@@ -40,6 +42,7 @@ func TestAddTargetInfo(t *testing.T) {
 				// scope should not be copied to target_info
 				sm := rm.ScopeMetrics().AppendEmpty()
 				sm.Scope().SetName("myscope")
+				sm.Scope().SetVersion("v0.0.1")
 
 				// other metrics should not be copied to target_info
 				metric := sm.Metrics().AppendEmpty()
@@ -63,9 +66,97 @@ func TestAddTargetInfo(t *testing.T) {
 				return rms
 			}(),
 		},
+		{
+			name:     "add scope info from scope metrics",
+			testFunc: AddScopeInfoMetric,
+			input: func() pmetric.Metrics {
+				metrics := pmetric.NewMetrics()
+				rm := metrics.ResourceMetrics().AppendEmpty()
+
+				// foo-label should be copied to target_info, not locationLabel
+				rm.Resource().Attributes().PutStr(locationLabel, "us-east")
+				rm.Resource().Attributes().PutStr("foo-label", "bar")
+
+				// scope should not be copied to target_info
+				sm := rm.ScopeMetrics().AppendEmpty()
+				sm.Scope().SetName("myscope")
+				sm.Scope().SetVersion("v0.0.1")
+
+				// other metrics should not be copied to target_info
+				metric := sm.Metrics().AppendEmpty()
+				metric.SetName("baz-metric")
+				metric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(2112)
+				return metrics
+			}(),
+			expected: func() pmetric.ResourceMetricsSlice {
+				rms := pmetric.NewResourceMetricsSlice()
+				rm := rms.AppendEmpty()
+
+				scopeInfo := rm.ScopeMetrics().AppendEmpty()
+				scopeInfoMetric := scopeInfo.Metrics().AppendEmpty()
+				scopeInfoMetric.SetName("otel_scope_info")
+				scopeInfoMetric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(1)
+				scopeInfoMetric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
+				scopeInfoMetric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
+				return rms
+			}(),
+		},
+		{
+			name: "add both scope info and target info",
+			testFunc: func(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
+				extraMetrics := AddTargetInfoMetric(m)
+				scopeInfoMetrics := AddScopeInfoMetric(m)
+				for i := 0; i < scopeInfoMetrics.Len(); i++ {
+					scopeInfoMetrics.At(i).ScopeMetrics().MoveAndAppendTo(extraMetrics.At(i).ScopeMetrics())
+				}
+				return extraMetrics
+			},
+			input: func() pmetric.Metrics {
+				metrics := pmetric.NewMetrics()
+				rm := metrics.ResourceMetrics().AppendEmpty()
+
+				// foo-label should be copied to target_info, not locationLabel
+				rm.Resource().Attributes().PutStr(locationLabel, "us-east")
+				rm.Resource().Attributes().PutStr("foo-label", "bar")
+
+				// scope should not be copied to target_info
+				sm := rm.ScopeMetrics().AppendEmpty()
+				sm.Scope().SetName("myscope")
+				sm.Scope().SetVersion("v0.0.1")
+
+				// other metrics should not be copied to target_info
+				metric := sm.Metrics().AppendEmpty()
+				metric.SetName("baz-metric")
+				metric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(2112)
+				return metrics
+			}(),
+			expected: func() pmetric.ResourceMetricsSlice {
+				rms := pmetric.NewResourceMetricsSlice()
+				rm := rms.AppendEmpty()
+
+				// resource attributes will be changed to a MonitoredResource on export, not in AddTargetInfo
+				// therefore they should still be present here
+				rm.Resource().Attributes().PutStr(locationLabel, "us-east")
+				rm.Resource().Attributes().PutStr("foo-label", "bar")
+
+				sm := rm.ScopeMetrics().AppendEmpty()
+				metric := sm.Metrics().AppendEmpty()
+				metric.SetName("target_info")
+				metric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(1)
+				metric.Gauge().DataPoints().At(0).Attributes().PutStr("foo-label", "bar")
+
+				scopeInfo := rm.ScopeMetrics().AppendEmpty()
+				scopeInfoMetric := scopeInfo.Metrics().AppendEmpty()
+				scopeInfoMetric.SetName("otel_scope_info")
+				scopeInfoMetric.SetEmptyGauge().DataPoints().AppendEmpty().SetIntValue(1)
+				scopeInfoMetric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_name", "myscope")
+				scopeInfoMetric.Gauge().DataPoints().At(0).Attributes().PutStr("otel_scope_version", "v0.0.1")
+				return rms
+			}(),
+		},
 	} {
 		t.Run(tc.name, func(t *testing.T) {
-			rms := AddTargetInfo(tc.input)
+			rms := tc.testFunc(tc.input)
 			assert.EqualValues(t, tc.expected, rms)
 		})
 	}
diff --git a/exporter/collector/integrationtest/config/config_test.go b/exporter/collector/integrationtest/config/config_test.go
index ac8a213..52e4916 100644
--- a/exporter/collector/integrationtest/config/config_test.go
+++ b/exporter/collector/integrationtest/config/config_test.go
@@ -82,7 +82,8 @@ func TestLoadConfig(t *testing.T) {
 					ClientConfig: collector.ClientConfig{
 						GRPCPoolSize: 1,
 					},
-					DefaultLogName: "foo-log",
+					DefaultLogName:        "foo-log",
+					ServiceResourceLabels: true,
 				},
 			},
 		})
diff --git a/exporter/collector/integrationtest/testcases/testcases_metrics.go b/exporter/collector/integrationtest/testcases/testcases_metrics.go
index 7d72044..1520c1d 100644
--- a/exporter/collector/integrationtest/testcases/testcases_metrics.go
+++ b/exporter/collector/integrationtest/testcases/testcases_metrics.go
@@ -17,6 +17,7 @@ package testcases
 import (
 	"strings"
 
+	"go.opentelemetry.io/collector/pdata/pmetric"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/sdk/metric/metricdata"
 
@@ -133,7 +134,14 @@ var MetricsTestCases = []TestCase{
 			cfg.MetricConfig.SkipCreateMetricDescriptor = true
 			cfg.MetricConfig.GetMetricName = googlemanagedprometheus.GetMetricName
 			cfg.MetricConfig.MapMonitoredResource = googlemanagedprometheus.MapToPrometheusTarget
-			cfg.MetricConfig.ExtraMetrics = googlemanagedprometheus.AddTargetInfo
+			cfg.MetricConfig.ExtraMetrics = func(m pmetric.Metrics) pmetric.ResourceMetricsSlice {
+				extraMetrics := googlemanagedprometheus.AddTargetInfoMetric(m)
+				scopeInfoMetrics := googlemanagedprometheus.AddScopeInfoMetric(m)
+				for i := 0; i < scopeInfoMetrics.Len(); i++ {
+					scopeInfoMetrics.At(i).ScopeMetrics().MoveAndAppendTo(extraMetrics.At(i).ScopeMetrics())
+				}
+				return extraMetrics
+			}
 			cfg.MetricConfig.InstrumentationLibraryLabels = false
 			cfg.MetricConfig.ServiceResourceLabels = false
 			cfg.MetricConfig.EnableSumOfSquaredDeviation = true
diff --git a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access.json b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access.json
index 3ab6ec5..b84d0f6 100644
--- a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access.json
+++ b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access.json
@@ -27,6 +27,12 @@
                     "stringValue": "test.log"
                   }
                 },
+                {
+                  "key": "single-attribute",
+                  "value": {
+                    "stringValue": "foobar"
+                  }
+                },
                 {
                   "key": "gcp.http_request",
                   "value": {
diff --git a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_expected.json b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_expected.json
index 8cd93b9..5c7934a 100644
--- a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_expected.json
+++ b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_expected.json
@@ -22,7 +22,8 @@
             "protocol": "HTTP/1.1"
           },
           "labels": {
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "single-attribute": "foobar"
           }
         },
         {
diff --git a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes.json b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes.json
index ada3e16..64b6217 100644
--- a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes.json
+++ b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes.json
@@ -14,6 +14,24 @@
             "value": {
               "stringValue": "baz"
             }
+          },
+          {
+            "key": "service.name",
+            "value": {
+              "stringValue": "my_service_name"
+            }
+          },
+          {
+            "key": "service.namespace",
+            "value": {
+              "stringValue": "my_service_namespace"
+            }
+          },
+          {
+            "key": "service.instance.id",
+            "value": {
+              "stringValue": "my_service_instance_id"
+            }
           }
         ]
       },
diff --git a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes_expected.json b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes_expected.json
index 78cf820..c980c0d 100644
--- a/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes_expected.json
+++ b/exporter/collector/integrationtest/testdata/fixtures/logs/logs_apache_access_resource_attributes_expected.json
@@ -23,7 +23,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -47,7 +50,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -71,7 +77,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -95,7 +104,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -119,7 +131,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -143,7 +158,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -167,7 +185,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -191,7 +212,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -215,7 +239,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -239,7 +266,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -263,7 +293,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -287,7 +320,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -311,7 +347,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -335,7 +374,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -359,7 +401,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -383,7 +428,10 @@
           },
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         },
         {
@@ -399,7 +447,10 @@
           "timestamp": "1970-01-01T00:00:00Z",
           "labels": {
             "custom_foobar": "baz",
-            "log.file.name": "test.log"
+            "log.file.name": "test.log",
+            "service_instance_id": "my_service_instance_id",
+            "service_name": "my_service_name",
+            "service_namespace": "my_service_namespace"
           }
         }
       ],
diff --git a/exporter/collector/integrationtest/testdata/fixtures/metrics/google_managed_prometheus_expect.json b/exporter/collector/integrationtest/testdata/fixtures/metrics/google_managed_prometheus_expect.json
index 0266ea5..9482e59 100644
--- a/exporter/collector/integrationtest/testdata/fixtures/metrics/google_managed_prometheus_expect.json
+++ b/exporter/collector/integrationtest/testdata/fixtures/metrics/google_managed_prometheus_expect.json
@@ -677,6 +677,37 @@
               }
             }
           ]
+        },
+        {
+          "metric": {
+            "type": "prometheus.googleapis.com/otel_scope_info/gauge",
+            "labels": {
+              "otel_scope_name": "",
+              "otel_scope_version": ""
+            }
+          },
+          "resource": {
+            "type": "prometheus_target",
+            "labels": {
+              "cluster": "rabbitmq-test-dev",
+              "instance": "10.92.5.2:15692",
+              "job": "demo",
+              "location": "us-central1-c",
+              "namespace": "default"
+            }
+          },
+          "metricKind": "GAUGE",
+          "valueType": "INT64",
+          "points": [
+            {
+              "interval": {
+                "endTime": "1970-01-01T00:00:00Z"
+              },
+              "value": {
+                "int64Value": "1"
+              }
+            }
+          ]
         }
       ]
     }
@@ -703,7 +734,7 @@
                   "startTime": "1970-01-01T00:00:00Z"
                 },
                 "value": {
-                  "int64Value": "20"
+                  "int64Value": "21"
                 }
               }
             ]