diff --git a/libdd-common/src/azure_app_services.rs b/libdd-common/src/azure_app_services.rs index 302b15d8c0..06b72375bc 100644 --- a/libdd-common/src/azure_app_services.rs +++ b/libdd-common/src/azure_app_services.rs @@ -263,6 +263,53 @@ impl AzureMetadata { pub fn get_function_runtime_version(&self) -> &str { get_value_or_unknown!(self.function_runtime_version) } + + /// Returns Azure App Services tags as an iterator of (tag_name, tag_value) tuples. + /// These tags are specific to Azure App Services. + pub fn get_app_service_tags(&self) -> impl ExactSizeIterator { + [ + ( + "aas.environment.extension_version", + self.get_extension_version(), + ), + ("aas.environment.instance_id", self.get_instance_id()), + ("aas.environment.instance_name", self.get_instance_name()), + ("aas.environment.os", self.get_operating_system()), + ("aas.resource.group", self.get_resource_group()), + ("aas.resource.id", self.get_resource_id()), + ("aas.site.kind", self.get_site_kind()), + ("aas.site.name", self.get_site_name()), + ("aas.site.type", self.get_site_type()), + ("aas.subscription.id", self.get_subscription_id()), + ] + .into_iter() + } + + /// Returns Azure Functions tags as an iterator of (tag_name, tag_value) tuples. + /// These tags are specific to Azure Functions. + pub fn get_function_tags(&self) -> impl ExactSizeIterator { + [ + ("aas.environment.instance_id", self.get_instance_id()), + ("aas.environment.instance_name", self.get_instance_name()), + ("aas.environment.os", self.get_operating_system()), + ("aas.environment.runtime", self.get_runtime()), + ( + "aas.environment.runtime_version", + self.get_runtime_version(), + ), + ( + "aas.environment.function_runtime", + self.get_function_runtime_version(), + ), + ("aas.resource.group", self.get_resource_group()), + ("aas.resource.id", self.get_resource_id()), + ("aas.site.kind", self.get_site_kind()), + ("aas.site.name", self.get_site_name()), + ("aas.site.type", self.get_site_type()), + ("aas.subscription.id", self.get_subscription_id()), + ] + .into_iter() + } } pub static AAS_METADATA: LazyLock> = @@ -712,6 +759,158 @@ mod tests { assert_eq!(expected_runtime_version, metadata.get_runtime_version()); } + #[test] + fn test_get_app_service_tags() { + let expected_site_name = "my_site_name"; + let expected_resource_group = "my_resource_group"; + let expected_site_version = "v42"; + let expected_operating_system = "FreeBSD"; + let expected_instance_name = "my_instance_name"; + let expected_instance_id = "my_instance_id"; + let expected_subscription_id = "sub-123"; + let expected_resource_id = "/subscriptions/sub-123/resourcegroups/my_resource_group/providers/microsoft.web/sites/my_site_name"; + + let mocked_env = MockEnv::new(&[ + (WEBSITE_SITE_NAME, expected_site_name), + (WEBSITE_RESOURCE_GROUP, expected_resource_group), + (SITE_EXTENSION_VERSION, expected_site_version), + (WEBSITE_OS, expected_operating_system), + (INSTANCE_NAME, expected_instance_name), + (INSTANCE_ID, expected_instance_id), + (SERVICE_CONTEXT, "1"), + ( + WEBSITE_OWNER_NAME, + &format!("{}+rg-webspace", expected_subscription_id), + ), + ]); + + let metadata = AzureMetadata::new(mocked_env).unwrap(); + + // Collect tags into a HashMap for easy lookup + let tags: std::collections::HashMap<&str, &str> = metadata.get_app_service_tags().collect(); + + // Verify all 10 App Service tags are present + assert_eq!(tags.len(), 10); + assert_eq!(tags.get("aas.resource.id"), Some(&expected_resource_id)); + assert_eq!( + tags.get("aas.environment.extension_version"), + Some(&expected_site_version) + ); + assert_eq!( + tags.get("aas.environment.instance_id"), + Some(&expected_instance_id) + ); + assert_eq!( + tags.get("aas.environment.instance_name"), + Some(&expected_instance_name) + ); + assert_eq!( + tags.get("aas.environment.os"), + Some(&expected_operating_system) + ); + assert_eq!( + tags.get("aas.resource.group"), + Some(&expected_resource_group) + ); + assert_eq!(tags.get("aas.site.name"), Some(&expected_site_name)); + assert_eq!(tags.get("aas.site.kind"), Some(&"app")); + assert_eq!(tags.get("aas.site.type"), Some(&"app")); + assert_eq!( + tags.get("aas.subscription.id"), + Some(&expected_subscription_id) + ); + + // Verify runtime tags are NOT present + assert_eq!(tags.get("aas.environment.runtime"), None); + assert_eq!(tags.get("aas.environment.runtime_version"), None); + assert_eq!(tags.get("aas.environment.function_runtime"), None); + + // Verify it's an ExactSizeIterator + let iter = metadata.get_app_service_tags(); + assert_eq!(iter.len(), 10); + } + + #[test] + fn test_get_function_tags() { + let expected_site_name = "my_site_name"; + let expected_resource_group = "my_resource_group"; + let expected_operating_system = "FreeBSD"; + let expected_instance_name = "my_instance_name"; + let expected_instance_id = "my_instance_id"; + let expected_function_extension_version = "~4"; + let expected_runtime = "node"; + let expected_runtime_version = "18"; + let expected_subscription_id = "sub-123"; + let expected_resource_id = "/subscriptions/sub-123/resourcegroups/my_resource_group/providers/microsoft.web/sites/my_site_name"; + + let mocked_env = MockEnv::new(&[ + (WEBSITE_SITE_NAME, expected_site_name), + (WEBSITE_RESOURCE_GROUP, expected_resource_group), + (WEBSITE_OS, expected_operating_system), + (INSTANCE_NAME, expected_instance_name), + (INSTANCE_ID, expected_instance_id), + (SERVICE_CONTEXT, "1"), + ( + FUNCTIONS_EXTENSION_VERSION, + expected_function_extension_version, + ), + (FUNCTIONS_WORKER_RUNTIME, expected_runtime), + (FUNCTIONS_WORKER_RUNTIME_VERSION, expected_runtime_version), + ( + WEBSITE_OWNER_NAME, + &format!("{}+rg-webspace", expected_subscription_id), + ), + ]); + + let metadata = AzureMetadata::new(mocked_env).unwrap(); + + // Collect tags into a HashMap for easy lookup + let tags: std::collections::HashMap<&str, &str> = metadata.get_function_tags().collect(); + + // Verify all 12 Function tags are present + assert_eq!(tags.len(), 12); + assert_eq!(tags.get("aas.resource.id"), Some(&expected_resource_id)); + assert_eq!( + tags.get("aas.environment.instance_id"), + Some(&expected_instance_id) + ); + assert_eq!( + tags.get("aas.environment.instance_name"), + Some(&expected_instance_name) + ); + assert_eq!( + tags.get("aas.environment.os"), + Some(&expected_operating_system) + ); + assert_eq!(tags.get("aas.environment.runtime"), Some(&expected_runtime)); + assert_eq!( + tags.get("aas.environment.runtime_version"), + Some(&expected_runtime_version) + ); + assert_eq!( + tags.get("aas.environment.function_runtime"), + Some(&expected_function_extension_version) + ); + assert_eq!( + tags.get("aas.resource.group"), + Some(&expected_resource_group) + ); + assert_eq!(tags.get("aas.site.name"), Some(&expected_site_name)); + assert_eq!(tags.get("aas.site.kind"), Some(&"functionapp")); + assert_eq!(tags.get("aas.site.type"), Some(&"function")); + assert_eq!( + tags.get("aas.subscription.id"), + Some(&expected_subscription_id) + ); + + // Verify extension_version tag is NOT present + assert_eq!(tags.get("aas.environment.extension_version"), None); + + // Verify it's an ExactSizeIterator + let iter = metadata.get_function_tags(); + assert_eq!(iter.len(), 12); + } + #[test] fn test_get_trimmed_env_var_empty_string() { env::remove_var("TEST_VAR_NONE"); diff --git a/libdd-profiling/src/exporter/profile_exporter.rs b/libdd-profiling/src/exporter/profile_exporter.rs index 26f44452c7..7a7e0522c7 100644 --- a/libdd-profiling/src/exporter/profile_exporter.rs +++ b/libdd-profiling/src/exporter/profile_exporter.rs @@ -110,31 +110,9 @@ impl ProfileExporter { // Add Azure App Services tags if available if let Some(aas) = &*azure_app_services::AAS_METADATA { - let aas_tags = [ - ("aas.resource.id", aas.get_resource_id()), - ( - "aas.environment.extension_version", - aas.get_extension_version(), - ), - ("aas.environment.instance_id", aas.get_instance_id()), - ("aas.environment.instance_name", aas.get_instance_name()), - ("aas.environment.os", aas.get_operating_system()), - ("aas.resource.group", aas.get_resource_group()), - ("aas.site.name", aas.get_site_name()), - ("aas.site.kind", aas.get_site_kind()), - ("aas.site.type", aas.get_site_type()), - ("aas.subscription.id", aas.get_subscription_id()), - ]; - - // Avoid infallible allocation paths when adding the Azure tags. - // This is an upper bound since Tag::new can fail and we'll skip invalid tags. - tags.try_reserve(aas_tags.len())?; - - tags.extend( - aas_tags - .into_iter() - .filter_map(|(name, value)| Tag::new(name, value).ok()), - ); + let aas_tags_iter = aas.get_app_service_tags(); + tags.try_reserve(aas_tags_iter.len())?; + tags.extend(aas_tags_iter.filter_map(|(name, value)| Tag::new(name, value).ok())); } // Precompute the base tags string (includes configured tags + Azure App Services tags) diff --git a/libdd-trace-utils/src/trace_utils.rs b/libdd-trace-utils/src/trace_utils.rs index b9311061ca..83efef6e19 100644 --- a/libdd-trace-utils/src/trace_utils.rs +++ b/libdd-trace-utils/src/trace_utils.rs @@ -555,35 +555,11 @@ pub fn enrich_span_with_azure_function_metadata(span: &mut pb::Span) { } if let Some(aas_metadata) = &*azure_app_services::AAS_METADATA_FUNCTION { - let aas_tags = [ - ("aas.resource.id", aas_metadata.get_resource_id()), - ( - "aas.environment.instance_id", - aas_metadata.get_instance_id(), - ), - ( - "aas.environment.instance_name", - aas_metadata.get_instance_name(), - ), - ("aas.subscription.id", aas_metadata.get_subscription_id()), - ("aas.environment.os", aas_metadata.get_operating_system()), - ("aas.environment.runtime", aas_metadata.get_runtime()), - ( - "aas.environment.runtime_version", - aas_metadata.get_runtime_version(), - ), - ( - "aas.environment.function_runtime", - aas_metadata.get_function_runtime_version(), - ), - ("aas.resource.group", aas_metadata.get_resource_group()), - ("aas.site.name", aas_metadata.get_site_name()), - ("aas.site.kind", aas_metadata.get_site_kind()), - ("aas.site.type", aas_metadata.get_site_type()), - ]; - aas_tags.into_iter().for_each(|(name, value)| { - span.meta.insert(name.to_string(), value.to_string()); - }); + span.meta.extend( + aas_metadata + .get_function_tags() + .map(|(name, value)| (name.to_string(), value.to_string())), + ); } }