From a134be5094bdd446cc4e3edc7e757e2f25567ad7 Mon Sep 17 00:00:00 2001 From: Tianning Li Date: Wed, 29 Jul 2026 14:28:49 -0400 Subject: [PATCH 1/2] [SLES-2961] fix(traces): extract Binary _datadog attribute from SQS records SqsRecord::get_carrier() only checked string_value on the _datadog message attribute, so a Binary-typed attribute (as delivered by SNS raw message delivery) was silently ignored and the trace context was never extracted. Branch on data_type instead, base64-decoding the Binary case, matching the existing SnsRecord::get_carrier() behavior. --- .../invocation/triggers/sqs_event.rs | 45 +++++++++++++++++-- .../tests/payloads/sqs_event_binary.json | 27 +++++++++++ 2 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 bottlecap/tests/payloads/sqs_event_binary.json diff --git a/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs b/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs index eb81944eb..8e7a21ae7 100644 --- a/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs +++ b/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs @@ -1,5 +1,6 @@ use crate::config::aws::get_aws_partition_by_region; use crate::lifecycle::invocation::{ + base64_to_string, processor::MS_TO_NS, triggers::{ DATADOG_CARRIER_KEY, FUNCTION_TRIGGER_EVENT_SOURCE_TAG, ServiceNameResolver, Trigger, @@ -177,10 +178,24 @@ impl Trigger for SqsRecord { fn get_carrier(&self) -> HashMap { let carrier = HashMap::new(); - if let Some(ma) = self.message_attributes.get(DATADOG_CARRIER_KEY) - && let Some(string_value) = &ma.string_value - { - return serde_json::from_str(string_value).unwrap_or_default(); + if let Some(ma) = self.message_attributes.get(DATADOG_CARRIER_KEY) { + match ma.data_type.as_str() { + "String" => { + if let Some(string_value) = &ma.string_value { + return serde_json::from_str(string_value).unwrap_or_default(); + } + } + "Binary" => { + if let Some(binary_value) = &ma.binary_value + && let Ok(carrier) = base64_to_string(binary_value) + { + return serde_json::from_str(&carrier).unwrap_or_default(); + } + } + _ => { + debug!("Unsupported dataType in SQS message attribute"); + } + } } // Check for SNS event sent through SQS @@ -419,6 +434,28 @@ mod tests { assert_eq!(carrier, expected); } + #[test] + fn test_get_carrier_binary() { + let json = read_json_file("sqs_event_binary.json"); + let payload = serde_json::from_str(&json).expect("Failed to deserialize into Value"); + let event = SqsRecord::new(payload).expect("Failed to deserialize SqsRecord"); + let carrier = event.get_carrier(); + + let expected = HashMap::from([ + ( + "x-datadog-trace-id".to_string(), + "1111111111111111111".to_string(), + ), + ( + "x-datadog-parent-id".to_string(), + "2222222222222222222".to_string(), + ), + ("x-datadog-sampling-priority".to_string(), "1".to_string()), + ]); + + assert_eq!(carrier, expected); + } + #[test] fn test_get_carrier_from_sns() { let json = read_json_file("sns_sqs_event.json"); diff --git a/bottlecap/tests/payloads/sqs_event_binary.json b/bottlecap/tests/payloads/sqs_event_binary.json new file mode 100644 index 000000000..5e8dc27d3 --- /dev/null +++ b/bottlecap/tests/payloads/sqs_event_binary.json @@ -0,0 +1,27 @@ +{ + "Records": [ + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "Hello from SQS!", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": { + "_datadog": { + "binaryValue": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIxMTExMTExMTExMTExMTExMTExIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjIyMjIyMjIyMjIyMjIyMjIyMjIiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIn0=", + "stringListValues": [], + "binaryListValues": [], + "dataType": "Binary" + } + }, + "md5OfBody": "{{{md5_of_body}}}", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "awsRegion": "us-east-1" + } + ] +} From cd97dde593a97bfcb51bde571f93b8dab5c21a03 Mon Sep 17 00:00:00 2001 From: Tianning Li Date: Wed, 29 Jul 2026 14:43:52 -0400 Subject: [PATCH 2/2] fix(traces): match SQS message attribute dataType by prefix Match "String"/"Binary" on ma.data_type via starts_with instead of exact equality, since SQS/SNS dataType values may carry a custom label suffix (e.g. "String.foo"). Exact matching silently dropped the carrier for any custom-labeled attribute, regressing behavior that worked before this PR (previously stringValue was used whenever present, regardless of dataType). --- .../invocation/triggers/sqs_event.rs | 50 +++++++++++++------ .../payloads/sqs_event_custom_label.json | 27 ++++++++++ 2 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 bottlecap/tests/payloads/sqs_event_custom_label.json diff --git a/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs b/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs index 8e7a21ae7..c8f3eb70f 100644 --- a/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs +++ b/bottlecap/src/lifecycle/invocation/triggers/sqs_event.rs @@ -179,22 +179,22 @@ impl Trigger for SqsRecord { let carrier = HashMap::new(); if let Some(ma) = self.message_attributes.get(DATADOG_CARRIER_KEY) { - match ma.data_type.as_str() { - "String" => { - if let Some(string_value) = &ma.string_value { - return serde_json::from_str(string_value).unwrap_or_default(); - } + // dataType may carry a custom label suffix, e.g. "String.foo" or "Binary.foo". + if ma.data_type.starts_with("String") { + if let Some(string_value) = &ma.string_value { + return serde_json::from_str(string_value).unwrap_or_default(); } - "Binary" => { - if let Some(binary_value) = &ma.binary_value - && let Ok(carrier) = base64_to_string(binary_value) - { - return serde_json::from_str(&carrier).unwrap_or_default(); - } - } - _ => { - debug!("Unsupported dataType in SQS message attribute"); + } else if ma.data_type.starts_with("Binary") { + if let Some(binary_value) = &ma.binary_value + && let Ok(carrier) = base64_to_string(binary_value) + { + return serde_json::from_str(&carrier).unwrap_or_default(); } + } else { + debug!( + "Unsupported dataType in SQS message attribute: {}", + ma.data_type + ); } } @@ -456,6 +456,28 @@ mod tests { assert_eq!(carrier, expected); } + #[test] + fn test_get_carrier_binary_custom_label() { + let json = read_json_file("sqs_event_custom_label.json"); + let payload = serde_json::from_str(&json).expect("Failed to deserialize into Value"); + let event = SqsRecord::new(payload).expect("Failed to deserialize SqsRecord"); + let carrier = event.get_carrier(); + + let expected = HashMap::from([ + ( + "x-datadog-trace-id".to_string(), + "3333333333333333333".to_string(), + ), + ( + "x-datadog-parent-id".to_string(), + "4444444444444444444".to_string(), + ), + ("x-datadog-sampling-priority".to_string(), "1".to_string()), + ]); + + assert_eq!(carrier, expected); + } + #[test] fn test_get_carrier_from_sns() { let json = read_json_file("sns_sqs_event.json"); diff --git a/bottlecap/tests/payloads/sqs_event_custom_label.json b/bottlecap/tests/payloads/sqs_event_custom_label.json new file mode 100644 index 000000000..0501724a3 --- /dev/null +++ b/bottlecap/tests/payloads/sqs_event_custom_label.json @@ -0,0 +1,27 @@ +{ + "Records": [ + { + "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", + "receiptHandle": "MessageReceiptHandle", + "body": "Hello from SQS!", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1523232000000", + "SenderId": "123456789012", + "ApproximateFirstReceiveTimestamp": "1523232000001" + }, + "messageAttributes": { + "_datadog": { + "binaryValue": "eyJ4LWRhdGFkb2ctdHJhY2UtaWQiOiIzMzMzMzMzMzMzMzMzMzMzMzMzIiwieC1kYXRhZG9nLXBhcmVudC1pZCI6IjQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQiLCJ4LWRhdGFkb2ctc2FtcGxpbmctcHJpb3JpdHkiOiIxIn0=", + "stringListValues": [], + "binaryListValues": [], + "dataType": "Binary.custom" + } + }, + "md5OfBody": "{{{md5_of_body}}}", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "awsRegion": "us-east-1" + } + ] +}