Skip to content

Commit 190b609

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

1 file changed

Lines changed: 55 additions & 6 deletions

File tree

src/otlp/span_mutations.rs

Lines changed: 55 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,56 @@ 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+
// A handler span created from a custom ActivitySource (scope
1005+
// "<service>.Handler") that manually sets faas.invocation_id. The scope is
1006+
// not a recognized Lambda instrumentation scope, but the invocation id must
1007+
// still be honored so the trace is correlated and kept rather than discarded.
1008+
let invocation_id = "8f3c1d2e-aws-request-id";
9921009
store_event_payload(invocation_id, r#"{"test":"data"}"#);
9931010

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

9991048
super::process_trace_request(&mut request, &mut invocation_ids, &mut encoded_body);
10001049

10011050
assert!(
10021051
invocation_ids.is_empty(),
1003-
"invocation_ids should remain empty for non-lambda scopes"
1052+
"a non-lambda span without faas.invocation_id contributes no invocation ids"
10041053
);
10051054
}
10061055

0 commit comments

Comments
 (0)