From 33681af87128da6e56087e20adb2b080f0c28197 Mon Sep 17 00:00:00 2001 From: Daniel Schwartz-Narbone Date: Tue, 10 Feb 2026 17:21:50 -0500 Subject: [PATCH 1/3] feat: unify Azure tags --- libdd-common/src/azure_app_services.rs | 113 ++++++++++++++++++ .../src/exporter/profile_exporter.rs | 28 +---- libdd-trace-utils/src/trace_utils.rs | 36 ++---- 3 files changed, 123 insertions(+), 54 deletions(-) diff --git a/libdd-common/src/azure_app_services.rs b/libdd-common/src/azure_app_services.rs index 302b15d8c0..fd72fedaf9 100644 --- a/libdd-common/src/azure_app_services.rs +++ b/libdd-common/src/azure_app_services.rs @@ -263,6 +263,35 @@ impl AzureMetadata { pub fn get_function_runtime_version(&self) -> &str { get_value_or_unknown!(self.function_runtime_version) } + + /// Returns all Azure App Services tags as an iterator of (tag_name, tag_value) tuples. + pub fn get_all_tags(&self) -> impl ExactSizeIterator { + [ + ( + "aas.environment.extension_version", + self.get_extension_version(), + ), + ( + "aas.environment.function_runtime", + self.get_function_runtime_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.environment.runtime", self.get_runtime()), + ( + "aas.environment.runtime_version", + self.get_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 +741,90 @@ mod tests { assert_eq!(expected_runtime_version, metadata.get_runtime_version()); } + #[test] + fn test_get_all_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_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), + (SITE_EXTENSION_VERSION, expected_site_version), + (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_all_tags().collect(); + + // Verify all 13 tags are present + assert_eq!(tags.len(), 13); + 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.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 it's an ExactSizeIterator + let iter = metadata.get_all_tags(); + assert_eq!(iter.len(), 13); + } + #[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..a577c57ab1 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_all_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..ee08f71a26 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_all_tags() + .map(|(name, value)| (name.to_string(), value.to_string())), + ); } } @@ -1261,6 +1237,7 @@ mod tests { // This test primarily ensures the function doesn't skip non-apim spans if azure_app_services::AAS_METADATA_FUNCTION.is_some() { assert!(span.meta.contains_key("aas.resource.id")); + assert!(span.meta.contains_key("aas.environment.extension_version")); assert!(span.meta.contains_key("aas.environment.instance_id")); assert!(span.meta.contains_key("aas.environment.instance_name")); assert!(span.meta.contains_key("aas.subscription.id")); @@ -1284,6 +1261,7 @@ mod tests { // Verify no aas.* tags were added assert!(!span.meta.contains_key("aas.resource.id")); + assert!(!span.meta.contains_key("aas.environment.extension_version")); assert!(!span.meta.contains_key("aas.environment.instance_id")); assert!(!span.meta.contains_key("aas.environment.instance_name")); assert!(!span.meta.contains_key("aas.subscription.id")); From cc0ff55b692b4ef9e1494243b9cea865d6c70076 Mon Sep 17 00:00:00 2001 From: Kathie Huang Date: Tue, 17 Feb 2026 17:35:03 -0500 Subject: [PATCH 2/3] Separate AAS and Azure Functions tags --- libdd-common/src/azure_app_services.rs | 120 +++++++++++++++--- .../src/exporter/profile_exporter.rs | 2 +- libdd-trace-utils/src/trace_utils.rs | 4 +- 3 files changed, 105 insertions(+), 21 deletions(-) diff --git a/libdd-common/src/azure_app_services.rs b/libdd-common/src/azure_app_services.rs index fd72fedaf9..ce4f66a2e1 100644 --- a/libdd-common/src/azure_app_services.rs +++ b/libdd-common/src/azure_app_services.rs @@ -264,17 +264,31 @@ impl AzureMetadata { get_value_or_unknown!(self.function_runtime_version) } - /// Returns all Azure App Services tags as an iterator of (tag_name, tag_value) tuples. - pub fn get_all_tags(&self) -> impl ExactSizeIterator { + /// Returns Azure App Services tags as an iterator of (tag_name, tag_value) tuples. + /// These tags are specific to Azure App Services (web apps) environments. + pub fn get_app_service_tags(&self) -> impl ExactSizeIterator { [ ( "aas.environment.extension_version", self.get_extension_version(), ), - ( - "aas.environment.function_runtime", - self.get_function_runtime_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 (serverless) environments. + 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()), @@ -283,6 +297,10 @@ impl AzureMetadata { "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()), @@ -742,13 +760,83 @@ mod tests { } #[test] - fn test_get_all_tags() { + 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"; @@ -758,7 +846,6 @@ mod tests { 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), @@ -778,15 +865,11 @@ mod tests { let metadata = AzureMetadata::new(mocked_env).unwrap(); // Collect tags into a HashMap for easy lookup - let tags: std::collections::HashMap<&str, &str> = metadata.get_all_tags().collect(); + let tags: std::collections::HashMap<&str, &str> = metadata.get_function_tags().collect(); - // Verify all 13 tags are present - assert_eq!(tags.len(), 13); + // 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.extension_version"), - Some(&expected_site_version) - ); assert_eq!( tags.get("aas.environment.instance_id"), Some(&expected_instance_id) @@ -820,9 +903,12 @@ mod tests { 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_all_tags(); - assert_eq!(iter.len(), 13); + let iter = metadata.get_function_tags(); + assert_eq!(iter.len(), 12); } #[test] diff --git a/libdd-profiling/src/exporter/profile_exporter.rs b/libdd-profiling/src/exporter/profile_exporter.rs index a577c57ab1..7a7e0522c7 100644 --- a/libdd-profiling/src/exporter/profile_exporter.rs +++ b/libdd-profiling/src/exporter/profile_exporter.rs @@ -110,7 +110,7 @@ impl ProfileExporter { // Add Azure App Services tags if available if let Some(aas) = &*azure_app_services::AAS_METADATA { - let aas_tags_iter = aas.get_all_tags(); + 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())); } diff --git a/libdd-trace-utils/src/trace_utils.rs b/libdd-trace-utils/src/trace_utils.rs index ee08f71a26..83efef6e19 100644 --- a/libdd-trace-utils/src/trace_utils.rs +++ b/libdd-trace-utils/src/trace_utils.rs @@ -557,7 +557,7 @@ pub fn enrich_span_with_azure_function_metadata(span: &mut pb::Span) { if let Some(aas_metadata) = &*azure_app_services::AAS_METADATA_FUNCTION { span.meta.extend( aas_metadata - .get_all_tags() + .get_function_tags() .map(|(name, value)| (name.to_string(), value.to_string())), ); } @@ -1237,7 +1237,6 @@ mod tests { // This test primarily ensures the function doesn't skip non-apim spans if azure_app_services::AAS_METADATA_FUNCTION.is_some() { assert!(span.meta.contains_key("aas.resource.id")); - assert!(span.meta.contains_key("aas.environment.extension_version")); assert!(span.meta.contains_key("aas.environment.instance_id")); assert!(span.meta.contains_key("aas.environment.instance_name")); assert!(span.meta.contains_key("aas.subscription.id")); @@ -1261,7 +1260,6 @@ mod tests { // Verify no aas.* tags were added assert!(!span.meta.contains_key("aas.resource.id")); - assert!(!span.meta.contains_key("aas.environment.extension_version")); assert!(!span.meta.contains_key("aas.environment.instance_id")); assert!(!span.meta.contains_key("aas.environment.instance_name")); assert!(!span.meta.contains_key("aas.subscription.id")); From 1c8e16da84a15792bc45354adbe6b66f3dd9a811 Mon Sep 17 00:00:00 2001 From: Kathie Huang Date: Tue, 17 Feb 2026 19:06:38 -0500 Subject: [PATCH 3/3] Fix comments --- libdd-common/src/azure_app_services.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libdd-common/src/azure_app_services.rs b/libdd-common/src/azure_app_services.rs index ce4f66a2e1..06b72375bc 100644 --- a/libdd-common/src/azure_app_services.rs +++ b/libdd-common/src/azure_app_services.rs @@ -265,7 +265,7 @@ impl AzureMetadata { } /// Returns Azure App Services tags as an iterator of (tag_name, tag_value) tuples. - /// These tags are specific to Azure App Services (web apps) environments. + /// These tags are specific to Azure App Services. pub fn get_app_service_tags(&self) -> impl ExactSizeIterator { [ ( @@ -286,7 +286,7 @@ impl AzureMetadata { } /// Returns Azure Functions tags as an iterator of (tag_name, tag_value) tuples. - /// These tags are specific to Azure Functions (serverless) environments. + /// These tags are specific to Azure Functions. pub fn get_function_tags(&self) -> impl ExactSizeIterator { [ ("aas.environment.instance_id", self.get_instance_id()),