diff --git a/example/metric/sdk/example.go b/example/metric/sdk/example.go index ea271e05e..965d891f7 100644 --- a/example/metric/sdk/example.go +++ b/example/metric/sdk/example.go @@ -29,7 +29,7 @@ import ( "go.opentelemetry.io/otel/metric" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" ) type observedFloat struct { diff --git a/example/trace/http/client/client.go b/example/trace/http/client/client.go index 259dbb495..2738e0ebd 100644 --- a/example/trace/http/client/client.go +++ b/example/trace/http/client/client.go @@ -29,7 +29,7 @@ import ( "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" "go.opentelemetry.io/otel/trace" texporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace" diff --git a/exporter/collector/googlemanagedprometheus/monitoredresource.go b/exporter/collector/googlemanagedprometheus/monitoredresource.go index 8a04536bc..bfbe598b0 100644 --- a/exporter/collector/googlemanagedprometheus/monitoredresource.go +++ b/exporter/collector/googlemanagedprometheus/monitoredresource.go @@ -15,6 +15,8 @@ package googlemanagedprometheus import ( + "strings" + "go.opentelemetry.io/collector/pdata/pcommon" semconv "go.opentelemetry.io/collector/semconv/v1.18.0" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" @@ -32,6 +34,7 @@ const ( jobLabel = "job" serviceNamespaceLabel = "service_namespace" instanceLabel = "instance" + unknownServicePrefix = "unknown_service" ) // promTargetKeys are attribute keys which are used in the prometheus_target monitored resource. @@ -40,9 +43,9 @@ var promTargetKeys = map[string][]string{ locationLabel: {locationLabel, semconv.AttributeCloudAvailabilityZone, semconv.AttributeCloudRegion}, clusterLabel: {clusterLabel, semconv.AttributeK8SClusterName}, namespaceLabel: {namespaceLabel, semconv.AttributeK8SNamespaceName}, - jobLabel: {jobLabel, semconv.AttributeFaaSName, semconv.AttributeServiceName}, + jobLabel: {jobLabel, semconv.AttributeServiceName, semconv.AttributeFaaSName}, serviceNamespaceLabel: {semconv.AttributeServiceNamespace}, - instanceLabel: {instanceLabel, semconv.AttributeFaaSInstance, semconv.AttributeServiceInstanceID}, + instanceLabel: {instanceLabel, semconv.AttributeServiceInstanceID, semconv.AttributeFaaSInstance}, } func (c Config) MapToPrometheusTarget(res pcommon.Resource) *monitoredrespb.MonitoredResource { @@ -69,9 +72,27 @@ func (c Config) MapToPrometheusTarget(res pcommon.Resource) *monitoredrespb.Moni // getStringOrEmpty returns the value of the first key found, or the empty string. func getStringOrEmpty(attributes pcommon.Map, keys ...string) string { for _, k := range keys { - if val, ok := attributes.Get(k); ok { + // skip the attribute if it starts with unknown_service, since the SDK + // sets this by default. It is used as a fallback below if no other + // values are found. + if val, ok := attributes.Get(k); ok && !strings.HasPrefix(val.Str(), unknownServicePrefix) { + return val.Str() + } + } + if contains(keys, string(semconv.AttributeServiceName)) { + // the service name started with unknown_service, and was ignored above + if val, ok := attributes.Get(semconv.AttributeServiceName); ok { return val.Str() } } return "" } + +func contains(list []string, element string) bool { + for _, item := range list { + if item == element { + return true + } + } + return false +} diff --git a/exporter/collector/googlemanagedprometheus/monitoredresource_test.go b/exporter/collector/googlemanagedprometheus/monitoredresource_test.go index 47510be25..edff109cc 100644 --- a/exporter/collector/googlemanagedprometheus/monitoredresource_test.go +++ b/exporter/collector/googlemanagedprometheus/monitoredresource_test.go @@ -150,7 +150,7 @@ func TestMapToPrometheusTarget(t *testing.T) { desc: "Attributes from cloud run", resourceLabels: map[string]string{ "cloud.region": "us-central1", - "service.name": "service:unknown", + "service.name": "unknown_service:go", "faas.name": "my-cloud-run-service", "faas.instance": "1234759430923053489543203", }, diff --git a/exporter/collector/monitoredresource_test.go b/exporter/collector/monitoredresource_test.go index fed71b825..0753115f5 100644 --- a/exporter/collector/monitoredresource_test.go +++ b/exporter/collector/monitoredresource_test.go @@ -503,7 +503,7 @@ func TestResourceMetricsToMonitoringMonitoredResource(t *testing.T) { "cloud.provider": "gcp", "cloud.platform": "gcp_app_engine", "cloud.availability_zone": "my-zone", - "faas.id": "myinstanceid", + "faas.instance": "myinstanceid", "faas.name": "myhostname", "faas.version": "v1", }, @@ -517,6 +517,70 @@ func TestResourceMetricsToMonitoringMonitoredResource(t *testing.T) { }, }, }, + { + name: "Cloud Run Instance", + resourceLabels: map[string]string{ + "cloud.provider": "gcp", + "cloud.platform": "gcp_cloud_run", + "cloud.region": "my-region", + "faas.instance": "myinstanceid", + "faas.name": "myfaasname", + "faas.version": "v1", + }, + expectMr: &monitoredrespb.MonitoredResource{ + Type: "generic_task", + Labels: map[string]string{ + "job": "myfaasname", + "location": "my-region", + "namespace": "", + "task_id": "myinstanceid", + }, + }, + }, + { + name: "Cloud Run Instance with default service", + resourceLabels: map[string]string{ + "cloud.provider": "gcp", + "cloud.platform": "gcp_cloud_run", + "cloud.region": "my-region", + "service.name": "unknown_service:go", + "faas.instance": "myinstanceid", + "faas.name": "myfaasname", + "faas.version": "v1", + }, + expectMr: &monitoredrespb.MonitoredResource{ + Type: "generic_task", + Labels: map[string]string{ + "job": "myfaasname", + "location": "my-region", + "namespace": "", + "task_id": "myinstanceid", + }, + }, + }, + { + name: "Cloud Run Instance with custom service", + resourceLabels: map[string]string{ + "cloud.provider": "gcp", + "cloud.platform": "gcp_cloud_run", + "cloud.region": "my-region", + "service.name": "customservice", + "service.namespace": "customnamespace", + "service.instance.id": "customserviceid", + "faas.instance": "myinstanceid", + "faas.name": "myfaasname", + "faas.version": "v1", + }, + expectMr: &monitoredrespb.MonitoredResource{ + Type: "generic_task", + Labels: map[string]string{ + "job": "customservice", + "location": "my-region", + "namespace": "customnamespace", + "task_id": "customserviceid", + }, + }, + }, } for _, test := range tests { @@ -543,7 +607,7 @@ func TestResourceMetricsToLoggingMonitoredResource(t *testing.T) { "cloud.provider": "gcp", "cloud.platform": "gcp_app_engine", "cloud.availability_zone": "my-zone", - "faas.id": "myhostid", + "faas.instance": "myhostid", "faas.name": "myhostname", "faas.version": "v1", }, diff --git a/exporter/metric/metric_test.go b/exporter/metric/metric_test.go index ee168baef..4dbb9c687 100644 --- a/exporter/metric/metric_test.go +++ b/exporter/metric/metric_test.go @@ -29,7 +29,7 @@ import ( "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" "cloud.google.com/go/monitoring/apiv3/v2/monitoringpb" "github.com/stretchr/testify/assert" @@ -569,15 +569,16 @@ func TestResourceToMonitoredResourcepb(t *testing.T) { }, }, { - desc: "Cloud Run From Detector", + desc: "Cloud Run From Detector with default service", resource: resource.NewWithAttributes( semconv.SchemaURL, attribute.String("cloud.provider", "gcp"), attribute.String("cloud.platform", "gcp_cloud_run"), attribute.String("cloud.region", "utopia"), - attribute.String("faas.id", "bar"), + attribute.String("faas.instance", "bar"), attribute.String("faas.name", "x-service"), attribute.String("faas.version", "v1"), + attribute.String("service.name", "unknown_service:go"), ), expectedType: "generic_task", expectedLabels: map[string]string{ @@ -594,7 +595,7 @@ func TestResourceToMonitoredResourcepb(t *testing.T) { attribute.String("cloud.provider", "gcp"), attribute.String("cloud.platform", "gcp_cloud_functions"), attribute.String("cloud.region", "utopia"), - attribute.String("faas.id", "bar"), + attribute.String("faas.instance", "bar"), attribute.String("faas.name", "x-service"), attribute.String("faas.version", "v1"), ), @@ -613,7 +614,7 @@ func TestResourceToMonitoredResourcepb(t *testing.T) { attribute.String("cloud.provider", "gcp"), attribute.String("cloud.platform", "gcp_app_engine"), attribute.String("cloud.availability_zone", "utopia"), - attribute.String("faas.id", "bar"), + attribute.String("faas.instance", "bar"), attribute.String("faas.name", "x-service"), attribute.String("faas.version", "v1"), ), diff --git a/exporter/metric/option.go b/exporter/metric/option.go index 4cf0dbe50..c80e299a2 100644 --- a/exporter/metric/option.go +++ b/exporter/metric/option.go @@ -21,7 +21,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/metric/metricdata" - semconv "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" apioption "google.golang.org/api/option" ) diff --git a/internal/resourcemapping/resourcemapping.go b/internal/resourcemapping/resourcemapping.go index 31909af21..df389826f 100644 --- a/internal/resourcemapping/resourcemapping.go +++ b/internal/resourcemapping/resourcemapping.go @@ -17,46 +17,47 @@ package resourcemapping import ( "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.18.0" + semconv "go.opentelemetry.io/otel/semconv/v1.21.0" monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres" ) const ( ProjectIDAttributeKey = "gcp.project.id" - awsAccount = "aws_account" - awsEc2Instance = "aws_ec2_instance" - clusterName = "cluster_name" - containerName = "container_name" - gceInstance = "gce_instance" - genericNode = "generic_node" - genericTask = "generic_task" - instanceID = "instance_id" - job = "job" - k8sCluster = "k8s_cluster" - k8sContainer = "k8s_container" - k8sNode = "k8s_node" - k8sPod = "k8s_pod" - location = "location" - namespace = "namespace" - namespaceName = "namespace_name" - nodeID = "node_id" - nodeName = "node_name" - podName = "pod_name" - region = "region" - taskID = "task_id" - zone = "zone" - gaeInstance = "gae_instance" - gaeApp = "gae_app" - gaeModuleID = "module_id" - gaeVersionID = "version_id" - cloudRunRevision = "cloud_run_revision" - cloudFunction = "cloud_function" - cloudFunctionName = "function_name" - serviceName = "service_name" - configurationName = "configuration_name" - revisionName = "revision_name" - bmsInstance = "baremetalsolution.googleapis.com/Instance" + awsAccount = "aws_account" + awsEc2Instance = "aws_ec2_instance" + clusterName = "cluster_name" + containerName = "container_name" + gceInstance = "gce_instance" + genericNode = "generic_node" + genericTask = "generic_task" + instanceID = "instance_id" + job = "job" + k8sCluster = "k8s_cluster" + k8sContainer = "k8s_container" + k8sNode = "k8s_node" + k8sPod = "k8s_pod" + location = "location" + namespace = "namespace" + namespaceName = "namespace_name" + nodeID = "node_id" + nodeName = "node_name" + podName = "pod_name" + region = "region" + taskID = "task_id" + zone = "zone" + gaeInstance = "gae_instance" + gaeApp = "gae_app" + gaeModuleID = "module_id" + gaeVersionID = "version_id" + cloudRunRevision = "cloud_run_revision" + cloudFunction = "cloud_function" + cloudFunctionName = "function_name" + serviceName = "service_name" + configurationName = "configuration_name" + revisionName = "revision_name" + bmsInstance = "baremetalsolution.googleapis.com/Instance" + unknownServicePrefix = "unknown_service" ) var ( @@ -115,7 +116,7 @@ var ( }}, gaeModuleID: {otelKeys: []string{string(semconv.FaaSNameKey)}}, gaeVersionID: {otelKeys: []string{string(semconv.FaaSVersionKey)}}, - instanceID: {otelKeys: []string{string(semconv.FaaSIDKey)}}, + instanceID: {otelKeys: []string{string(semconv.FaaSInstanceKey)}}, }, gaeApp: { location: {otelKeys: []string{ @@ -149,7 +150,7 @@ var ( }, namespace: {otelKeys: []string{string(semconv.ServiceNamespaceKey)}}, job: {otelKeys: []string{string(semconv.ServiceNameKey), string(semconv.FaaSNameKey)}}, - taskID: {otelKeys: []string{string(semconv.ServiceInstanceIDKey), string(semconv.FaaSIDKey)}}, + taskID: {otelKeys: []string{string(semconv.ServiceInstanceIDKey), string(semconv.FaaSInstanceKey)}}, }, genericNode: { location: { @@ -229,8 +230,8 @@ func commonResourceAttributesToMonitoredResource(cloudPlatform string, attrs Rea _, hasServiceName := attrs.GetString(string(semconv.ServiceNameKey)) _, hasFaaSName := attrs.GetString(string(semconv.FaaSNameKey)) _, hasServiceInstanceID := attrs.GetString(string(semconv.ServiceInstanceIDKey)) - _, hasFaaSID := attrs.GetString(string(semconv.FaaSIDKey)) - if (hasServiceName && hasServiceInstanceID) || (hasFaaSID && hasFaaSName) { + _, hasFaaSInstance := attrs.GetString(string(semconv.FaaSInstanceKey)) + if (hasServiceName && hasServiceInstanceID) || (hasFaaSInstance && hasFaaSName) { return createMonitoredResource(genericTask, attrs) } @@ -252,10 +253,14 @@ func createMonitoredResource( // Coalesce the possible keys in order for _, otelKey := range mappingConfig.otelKeys { mrValue, ok = resourceAttrs.GetString(otelKey) - if mrValue != "" { + if mrValue != "" && !strings.HasPrefix(mrValue, unknownServicePrefix) { break } } + if mrValue == "" && contains(mappingConfig.otelKeys, string(semconv.ServiceNameKey)) { + // the service name started with unknown_service, and was ignored above + mrValue, ok = resourceAttrs.GetString(string(semconv.ServiceNameKey)) + } if !ok || mrValue == "" { mrValue = mappingConfig.fallbackLiteral } @@ -267,6 +272,15 @@ func createMonitoredResource( } } +func contains(list []string, element string) bool { + for _, item := range list { + if item == element { + return true + } + } + return false +} + func sanitizeUTF8(s string) string { return strings.ToValidUTF8(s, "�") }