Skip to content

Commit cb6dae9

Browse files
fix: honor faas.invocation_id from non-Lambda instrumentation scopes
Manual instrumentation that creates its handler span from a custom ActivitySource/Tracer (a scope not in the recognized AWS Lambda instrumentation list) had its faas.invocation_id ignored: process_trace_request only extracted the invocation id from spans in recognized Lambda scopes. With no id and no Runtime API proxy fallback, the receiver discarded the trace ("/v1/traces has no invocation IDs, discarding trace"). Extract faas.invocation_id from spans in any scope for invocation correlation. Handler-span rewrites (rename, kind, reparent) stay limited to recognized Lambda scopes, so non-Lambda spans are correlated but otherwise left untouched. Refs: SUP-1255 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 622fcb1 commit cb6dae9

1 file changed

Lines changed: 56 additions & 6 deletions

File tree

src/otlp/span_mutations.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,19 @@ pub fn process_trace_request(
644644
for span in &mut scope_span.spans {
645645
add_resource_attributes(span);
646646
extract_http_body_logs(span);
647+
// Honor an explicitly set faas.invocation_id even when the span
648+
// comes from a non-Lambda instrumentation scope (e.g. a custom
649+
// ActivitySource/Tracer used for manual instrumentation). This is
650+
// used only to correlate the trace to an invocation; the
651+
// handler-span rewrites (rename, kind, reparent) below stay
652+
// limited to recognized Lambda instrumentation scopes. Without
653+
// this, a manually set faas.invocation_id is ignored and the
654+
// trace can be discarded with "no invocation IDs".
655+
if let Some(invocation_id) = extract_invocation_id(span) {
656+
if !invocation_ids.contains(&invocation_id) {
657+
invocation_ids.push(invocation_id);
658+
}
659+
}
647660
}
648661
continue;
649662
}
@@ -670,7 +683,7 @@ pub fn process_trace_request(
670683

671684
#[cfg(test)]
672685
mod tests {
673-
use super::{build_synthetic_trace, StatusCode};
686+
use super::{build_synthetic_trace, SpanKind, StatusCode};
674687
use crate::state::invocation_data::StoredTrace;
675688
use crate::state::invocation_entry;
676689
use hyper::{header, Method};
@@ -987,20 +1000,57 @@ mod tests {
9871000

9881001
#[test]
9891002
#[serial]
990-
fn process_trace_request_ignores_non_lambda_scopes() {
991-
let invocation_id = "inv-process-6";
1003+
fn process_trace_request_honors_invocation_id_in_non_lambda_scope_without_rewrite() {
1004+
// Reproduces the customer scenario (SUP-1255): a handler span created from a
1005+
// custom ActivitySource (scope "<service>.Handler") that manually sets
1006+
// faas.invocation_id. The scope is not a recognized Lambda instrumentation
1007+
// scope, but the invocation id must still be honored so the trace is
1008+
// correlated and kept rather than discarded.
1009+
let invocation_id = "8f3c1d2e-aws-request-id";
9921010
store_event_payload(invocation_id, r#"{"test":"data"}"#);
9931011

994-
let span = make_span_with_invocation(invocation_id);
995-
let mut request = make_request_with_scope("other.instrumentation.scope", span);
1012+
let mut span = make_span_with_invocation(invocation_id);
1013+
span.name = "GET /orders".to_string();
1014+
span.kind = SpanKind::Server as i32;
1015+
let mut request = make_request_with_scope("payments-api.Handler", span);
1016+
let mut invocation_ids = Vec::new();
1017+
let mut encoded_body = Vec::new();
1018+
1019+
super::process_trace_request(&mut request, &mut invocation_ids, &mut encoded_body);
1020+
1021+
// The invocation id is extracted even though the scope is not a Lambda scope.
1022+
assert_eq!(invocation_ids, vec![invocation_id.to_string()]);
1023+
1024+
// ...but the span itself is left untouched: handler-span rewrites stay
1025+
// limited to recognized Lambda instrumentation scopes.
1026+
let out = &request.resource_spans[0].scope_spans[0].spans[0];
1027+
assert_eq!(
1028+
out.name, "GET /orders",
1029+
"non-lambda span must not be renamed"
1030+
);
1031+
assert_eq!(
1032+
out.kind,
1033+
SpanKind::Server as i32,
1034+
"non-lambda span kind must be preserved"
1035+
);
1036+
}
1037+
1038+
#[test]
1039+
#[serial]
1040+
fn process_trace_request_non_lambda_span_without_invocation_id_adds_no_ids() {
1041+
let span = Span {
1042+
name: "GET /".to_string(),
1043+
..Default::default()
1044+
};
1045+
let mut request = make_request_with_scope("System.Net.Http", span);
9961046
let mut invocation_ids = Vec::new();
9971047
let mut encoded_body = Vec::new();
9981048

9991049
super::process_trace_request(&mut request, &mut invocation_ids, &mut encoded_body);
10001050

10011051
assert!(
10021052
invocation_ids.is_empty(),
1003-
"invocation_ids should remain empty for non-lambda scopes"
1053+
"a non-lambda span without faas.invocation_id contributes no invocation ids"
10041054
);
10051055
}
10061056

0 commit comments

Comments
 (0)