Skip to content

Commit a820699

Browse files
authored
fix(libdd-common): Add fallback logic for resolving Azure Functions instance name [SVLS-8931] (#2077)
# What does this PR do? - Moves logic for resolving Azure Functions instance name from [serverless-components](https://github.com/DataDog/serverless-components/blob/da82dd574ac4eae73afc7201b9781e057643c2d6/crates/datadog-metrics-collector/src/azure_instance.rs#L21) to libdd-common. - Logic for resolving instance name: `COMPUTERNAME` -> `WEBSITE_POD_NAME` -> `CONTAINER_NAME` - This matches the `instance` tag of `azure.functions.function_execution_count` integration metric - Before, we only checked `COMPUTERNAME`. The investigation to determine which env vars should be used is in the Instance tab of [Enhanced Metrics in the Serverless Compatibility Layer](https://docs.google.com/document/d/1uMT4fphW7C31JLmf5KY1CdrXGPkn2QLfIvHZK3CJD40/edit?usp=sharing) - This has the added benefit of populating the instance name span attribute `aas.environment.instance_name` in hosting plans where the attribute was `unknown` before - Before, we only looked at `COMPUTERNAME` for instance name, which was empty for Linux Flex Consumption and Consumption functions - This PR also makes some consts public so that they can be used by serverless-components; see DataDog/serverless-components#134 for usage # Motivation https://datadoghq.atlassian.net/browse/SVLS-8931 https://datadoghq.atlassian.net/browse/SVLS-9015 # Additional Notes Once this PR is merged, we plan to make a new PR in serverless-components to update the libdatadog commit hash and remove the redundant consts and instance name logic. Draft PR: DataDog/serverless-components#134 # How to test the change? Unit tests: Run `cargo test -p libdd-common azure_app_services` 1. Use git log to find this PR's most recent commit hash 2. Clone [serverless-components](https://github.com/DataDog/serverless-components/tree/main) and update the commit hash in [datadog-trace-agent/Cargo.toml](https://github.com/DataDog/serverless-components/blob/main/crates/datadog-trace-agent/Cargo.toml) everywhere that libdatadog is used 3. Follow the instructions in the [Serverless Compatibility Layer docs](https://datadoghq.atlassian.net/wiki/spaces/SLS/pages/2977497119/Serverless+Compatibility+Layer) to deploy sample apps Flex Consumption function with fix: <img width="956" height="568" alt="image" src="https://github.com/user-attachments/assets/b11585cf-7e1c-4d89-a60d-490bfe6bf45e" /> Flex Consumption function without fix: <img width="1267" height="504" alt="Screenshot 2026-06-03 at 4 33 58 PM" src="https://github.com/user-attachments/assets/252c9ffa-3918-457f-95b5-adc1b794aa2b" /> Consumption function with fix: <img width="974" height="564" alt="image" src="https://github.com/user-attachments/assets/2d92d604-be97-4eb1-8e6a-9d048420c715" /> Consumption function without fix: <img width="1276" height="534" alt="image" src="https://github.com/user-attachments/assets/b9918da9-c177-45df-8432-92f7eee001eb" /> I tested by deploying with [serverless-compat-self-monitoring](https://github.com/DataDog/serverless-compat-self-monitoring): - FC1 and Y1 linux functions get the `aas.environment.instance_name` span attribute populated - The span attributes for all other functions still look the same (same shape and casing) Co-authored-by: kathie.huang <kathie.huang@datadoghq.com>
1 parent 1ce0b30 commit a820699

1 file changed

Lines changed: 109 additions & 13 deletions

File tree

libdd-common/src/azure_app_services.rs

Lines changed: 109 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@ const WEBSITE_SITE_NAME: &str = "WEBSITE_SITE_NAME";
1111
const WEBSITE_RESOURCE_GROUP: &str = "WEBSITE_RESOURCE_GROUP";
1212
const SITE_EXTENSION_VERSION: &str = "DD_AAS_DOTNET_EXTENSION_VERSION";
1313
const WEBSITE_OS: &str = "WEBSITE_OS";
14-
const INSTANCE_NAME: &str = "COMPUTERNAME";
14+
const COMPUTERNAME: &str = "COMPUTERNAME";
15+
const CONTAINER_NAME: &str = "CONTAINER_NAME";
16+
const WEBSITE_POD_NAME: &str = "WEBSITE_POD_NAME";
1517
const INSTANCE_ID: &str = "WEBSITE_INSTANCE_ID";
1618
const SERVICE_CONTEXT: &str = "DD_AZURE_APP_SERVICES";
1719
const FUNCTIONS_WORKER_RUNTIME: &str = "FUNCTIONS_WORKER_RUNTIME";
1820
const FUNCTIONS_WORKER_RUNTIME_VERSION: &str = "FUNCTIONS_WORKER_RUNTIME_VERSION";
1921
const FUNCTIONS_EXTENSION_VERSION: &str = "FUNCTIONS_EXTENSION_VERSION";
2022
const DD_AZURE_RESOURCE_GROUP: &str = "DD_AZURE_RESOURCE_GROUP";
21-
const WEBSITE_SKU: &str = "WEBSITE_SKU";
23+
pub const WEBSITE_SKU: &str = "WEBSITE_SKU";
24+
pub const REGION_NAME: &str = "REGION_NAME";
2225

23-
const UNKNOWN_VALUE: &str = "unknown";
26+
pub const UNKNOWN_VALUE: &str = "unknown";
2427

2528
enum AzureContext {
2629
AzureFunctions,
@@ -63,7 +66,9 @@ const AAS_VAR_NAMES: &[&str] = &[
6366
WEBSITE_RESOURCE_GROUP,
6467
SITE_EXTENSION_VERSION,
6568
WEBSITE_OS,
66-
INSTANCE_NAME,
69+
COMPUTERNAME,
70+
CONTAINER_NAME,
71+
WEBSITE_POD_NAME,
6772
INSTANCE_ID,
6873
SERVICE_CONTEXT,
6974
FUNCTIONS_WORKER_RUNTIME,
@@ -223,12 +228,14 @@ impl AzureMetadata {
223228
_ => ("app".to_owned(), "app".to_owned()),
224229
};
225230

231+
let website_sku = query.get_var(WEBSITE_SKU);
232+
226233
let resource_group = query
227234
.get_var(DD_AZURE_RESOURCE_GROUP)
228235
.or_else(|| query.get_var(WEBSITE_RESOURCE_GROUP))
229236
.or_else(|| {
230237
// Check if we're in flex consumption plan first
231-
match query.get_var(WEBSITE_SKU).as_deref() {
238+
match website_sku.as_deref() {
232239
Some("FlexConsumption") => None,
233240
/* Flex Consumption plans need the `DD_AZURE_RESOURCE_GROUP` env var. If this
234241
* logic ever changes, update the logic in
@@ -247,7 +254,16 @@ impl AzureMetadata {
247254
let operating_system = query
248255
.get_var(WEBSITE_OS)
249256
.unwrap_or(std::env::consts::OS.to_string());
250-
let instance_name = query.get_var(INSTANCE_NAME);
257+
258+
let computer_name = query.get_var(COMPUTERNAME);
259+
let pod_name = query.get_var(WEBSITE_POD_NAME);
260+
let container_name = query.get_var(CONTAINER_NAME);
261+
let instance_name = resolve_instance_name(
262+
computer_name.as_deref(),
263+
pod_name.as_deref(),
264+
container_name.as_deref(),
265+
);
266+
251267
let instance_id = query.get_var(INSTANCE_ID);
252268

253269
let runtime = query.get_var(FUNCTIONS_WORKER_RUNTIME);
@@ -394,6 +410,22 @@ impl AzureMetadata {
394410
}
395411
}
396412

413+
/// Resolves the instance name to match the Azure integration metric's `instance` tag.
414+
fn resolve_instance_name(
415+
computer_name: Option<&str>,
416+
pod_name: Option<&str>,
417+
container_name: Option<&str>,
418+
) -> Option<String> {
419+
fn non_empty(s: Option<&str>) -> Option<&str> {
420+
s.map(|v| v.trim()).filter(|v| !v.is_empty())
421+
}
422+
423+
non_empty(computer_name)
424+
.or_else(|| non_empty(pod_name))
425+
.or_else(|| non_empty(container_name))
426+
.map(|s| s.to_string())
427+
}
428+
397429
pub static AAS_METADATA: LazyLock<Option<AzureMetadata>> =
398430
LazyLock::new(|| AzureMetadata::new(RealEnv {}));
399431

@@ -636,10 +668,10 @@ mod tests {
636668
"00000000-0000-0000-0000-000000000000+flex-EastUSwebspace-Linux",
637669
),
638670
(WEBSITE_SKU, "FlexConsumption"),
639-
(SERVICE_CONTEXT, "1"),
671+
(FUNCTIONS_WORKER_RUNTIME, "node"),
640672
]);
641673

642-
let metadata = AzureMetadata::new(mocked_env).unwrap();
674+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
643675

644676
assert_eq!(metadata.get_resource_group(), UNKNOWN_VALUE);
645677
}
@@ -653,10 +685,10 @@ mod tests {
653685
),
654686
(DD_AZURE_RESOURCE_GROUP, "test-flex-rg"),
655687
(WEBSITE_SKU, "FlexConsumption"),
656-
(SERVICE_CONTEXT, "1"),
688+
(FUNCTIONS_WORKER_RUNTIME, "node"),
657689
]);
658690

659-
let metadata = AzureMetadata::new(mocked_env).unwrap();
691+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
660692

661693
// Should use the DD_AZURE_RESOURCE_GROUP value instead of extracting from
662694
// WEBSITE_OWNER_NAME
@@ -811,7 +843,7 @@ mod tests {
811843
(WEBSITE_RESOURCE_GROUP, expected_resource_group.as_str()),
812844
(SITE_EXTENSION_VERSION, expected_site_version.as_str()),
813845
(WEBSITE_OS, expected_operating_system.as_str()),
814-
(INSTANCE_NAME, expected_instance_name.as_str()),
846+
(COMPUTERNAME, expected_instance_name.as_str()),
815847
(INSTANCE_ID, expected_instance_id.as_str()),
816848
(SERVICE_CONTEXT, "1"),
817849
(
@@ -857,7 +889,7 @@ mod tests {
857889
(WEBSITE_RESOURCE_GROUP, expected_resource_group),
858890
(SITE_EXTENSION_VERSION, expected_site_version),
859891
(WEBSITE_OS, expected_operating_system),
860-
(INSTANCE_NAME, expected_instance_name),
892+
(COMPUTERNAME, expected_instance_name),
861893
(INSTANCE_ID, expected_instance_id),
862894
(SERVICE_CONTEXT, "1"),
863895
(
@@ -929,7 +961,7 @@ mod tests {
929961
(WEBSITE_SITE_NAME, expected_site_name),
930962
(WEBSITE_RESOURCE_GROUP, expected_resource_group),
931963
(WEBSITE_OS, expected_operating_system),
932-
(INSTANCE_NAME, expected_instance_name),
964+
(COMPUTERNAME, expected_instance_name),
933965
(INSTANCE_ID, expected_instance_id),
934966
(SERVICE_CONTEXT, "1"),
935967
(
@@ -1002,4 +1034,68 @@ mod tests {
10021034
None
10031035
);
10041036
}
1037+
1038+
#[test]
1039+
fn test_instance_name_computer_name_wins_over_pod_and_container() {
1040+
let mocked_env = MockEnv::new(&[
1041+
(FUNCTIONS_WORKER_RUNTIME, "node"),
1042+
(COMPUTERNAME, "10-20-30-40"),
1043+
(WEBSITE_POD_NAME, "pod-1"),
1044+
(CONTAINER_NAME, "container-1"),
1045+
]);
1046+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1047+
assert_eq!(metadata.get_instance_name(), "10-20-30-40");
1048+
}
1049+
1050+
#[test]
1051+
fn test_instance_name_pod_name_preferred_over_container_name() {
1052+
let mocked_env = MockEnv::new(&[
1053+
(FUNCTIONS_WORKER_RUNTIME, "node"),
1054+
(WEBSITE_POD_NAME, "0--abc-test"),
1055+
(CONTAINER_NAME, "container-1"),
1056+
]);
1057+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1058+
assert_eq!(metadata.get_instance_name(), "0--abc-test");
1059+
}
1060+
1061+
#[test]
1062+
fn test_instance_name_falls_back_to_container_name() {
1063+
let mocked_env = MockEnv::new(&[
1064+
(FUNCTIONS_WORKER_RUNTIME, "node"),
1065+
(CONTAINER_NAME, "0--abc-test"),
1066+
]);
1067+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1068+
assert_eq!(metadata.get_instance_name(), "0--abc-test");
1069+
}
1070+
1071+
#[test]
1072+
fn test_instance_name_empty_string_treated_as_missing() {
1073+
let mocked_env = MockEnv::new(&[
1074+
(FUNCTIONS_WORKER_RUNTIME, "node"),
1075+
(CONTAINER_NAME, ""),
1076+
(WEBSITE_POD_NAME, ""),
1077+
(COMPUTERNAME, "worker-1"),
1078+
]);
1079+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1080+
assert_eq!(metadata.get_instance_name(), "worker-1");
1081+
}
1082+
1083+
#[test]
1084+
fn test_instance_name_whitespace_only_treated_as_missing() {
1085+
let mocked_env = MockEnv::new(&[
1086+
(FUNCTIONS_WORKER_RUNTIME, "node"),
1087+
(CONTAINER_NAME, " "),
1088+
(WEBSITE_POD_NAME, " "),
1089+
(COMPUTERNAME, "worker-2"),
1090+
]);
1091+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1092+
assert_eq!(metadata.get_instance_name(), "worker-2");
1093+
}
1094+
1095+
#[test]
1096+
fn test_instance_name_no_instance_vars_is_unknown() {
1097+
let mocked_env = MockEnv::new(&[(FUNCTIONS_WORKER_RUNTIME, "node")]);
1098+
let metadata = AzureMetadata::new_function(mocked_env).unwrap();
1099+
assert_eq!(metadata.get_instance_name(), UNKNOWN_VALUE);
1100+
}
10051101
}

0 commit comments

Comments
 (0)