From be3cdc741695ed64bac82f47f56f759faa52664c Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:24:55 -0500 Subject: [PATCH 1/9] Adding context_id support to selectors Adding support for supplying context_id through selectors --- .../telemetry/config_new/router/selectors.rs | 58 ++++++++++++++++ .../config_new/subgraph/selectors.rs | 55 +++++++++++++++ .../config_new/supergraph/selectors.rs | 67 +++++++++++++++++++ 3 files changed, 180 insertions(+) diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index e2b9f8cee3..59e15e2f93 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -193,6 +193,11 @@ pub(crate) enum RouterSelector { /// The format of the trace ID. trace_id: TraceIdFormat, }, + /// The context ID of the request (unique per request). + ContextId { + /// The context ID + context_id: bool, + }, } impl Selector for RouterSelector { @@ -264,6 +269,9 @@ impl Selector for RouterSelector { insert_display_router_response(request); None } + RouterSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(request.context.id.clone())) + } // Related to Response _ => None, } @@ -399,6 +407,9 @@ impl Selector for RouterSelector { .ok() .flatten() .map(opentelemetry::Value::from), + RouterSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(response.context.id.clone())) + } _ => None, } } @@ -408,6 +419,9 @@ impl Selector for RouterSelector { RouterSelector::Error { .. } => Some(error.to_string().into()), RouterSelector::Static(val) => Some(val.clone().into()), RouterSelector::StaticField { r#static } => Some(r#static.clone().into()), + RouterSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(ctx.id.clone())) + } RouterSelector::ResponseContext { response_context, default, @@ -460,6 +474,7 @@ impl Selector for RouterSelector { | RouterSelector::Static(_) | RouterSelector::Env { .. } | RouterSelector::StaticField { .. } + | RouterSelector::ContextId { .. } ) } Stage::Response | Stage::ResponseEvent => matches!( @@ -478,6 +493,7 @@ impl Selector for RouterSelector { | RouterSelector::RouterOverhead { .. } | RouterSelector::ActiveSubgraphRequests { .. } | RouterSelector::OnGraphQLError { .. } + | RouterSelector::ContextId { .. } ), Stage::ResponseField => false, Stage::Error => matches!( @@ -491,6 +507,7 @@ impl Selector for RouterSelector { | RouterSelector::StaticField { .. } | RouterSelector::ResponseContext { .. } | RouterSelector::Error { .. } + | RouterSelector::ContextId { .. } ), Stage::Drop => matches!( self, @@ -1048,4 +1065,45 @@ mod test { r#"{"message":"Something went wrong","locations":[{"line":1,"column":1}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}"# ); } + + #[test] + fn router_context_id() { + let selector = RouterSelector::ContextId { context_id: true }; + let context = crate::context::Context::new(); + let expected_id = context.id.clone(); + + // Test on_request + let request_result = selector.on_request( + &crate::services::RouterRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ); + assert_eq!(request_result, Some(expected_id.clone().into())); + + // Test on_response + let response_result = selector.on_response( + &crate::services::RouterResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ); + assert_eq!(response_result, Some(expected_id.clone().into())); + + // Test on_error + let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); + assert_eq!(error_result, Some(expected_id.into())); + + // Test that context_id: false returns None + let selector_disabled = RouterSelector::ContextId { context_id: false }; + assert_eq!( + selector_disabled.on_request( + &crate::services::RouterRequest::fake_builder() + .context(context) + .build() + .unwrap(), + ), + None + ); + } } diff --git a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs index 61508af716..8d65f55cbc 100644 --- a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs @@ -289,6 +289,11 @@ pub(crate) enum SubgraphSelector { /// Select data you want from the computed cache control from response caching response_cache_control: CacheControlSelector, }, + /// The context ID of the request (unique per request). + ContextId { + /// The context ID + context_id: bool, + }, } impl Selector for SubgraphSelector { @@ -468,6 +473,9 @@ impl Selector for SubgraphSelector { } SubgraphSelector::Static(val) => Some(val.clone().into()), SubgraphSelector::StaticField { r#static } => Some(r#static.clone().into()), + SubgraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(request.context.id.clone())) + } // For response _ => None, @@ -742,6 +750,9 @@ impl Selector for SubgraphSelector { } }) } + SubgraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(response.context.id.clone())) + } // For request _ => None, } @@ -788,6 +799,9 @@ impl Selector for SubgraphSelector { .as_ref() .and_then(|v| v.maybe_to_otel_value()) .or_else(|| default.maybe_to_otel_value()), + SubgraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(ctx.id.clone())) + } _ => None, } } @@ -820,6 +834,7 @@ impl Selector for SubgraphSelector { | SubgraphSelector::Env { .. } | SubgraphSelector::Static(_) | SubgraphSelector::StaticField { .. } + | SubgraphSelector::ContextId { .. } ), Stage::Response => matches!( self, @@ -838,6 +853,7 @@ impl Selector for SubgraphSelector { | SubgraphSelector::Cache { .. } | SubgraphSelector::ResponseCache { .. } | SubgraphSelector::ResponseCacheControl { .. } + | SubgraphSelector::ContextId { .. } ), Stage::ResponseEvent => false, Stage::ResponseField => false, @@ -850,6 +866,7 @@ impl Selector for SubgraphSelector { | SubgraphSelector::Static(_) | SubgraphSelector::StaticField { .. } | SubgraphSelector::ResponseContext { .. } + | SubgraphSelector::ContextId { .. } ), Stage::Drop => matches!( self, @@ -2174,4 +2191,42 @@ mod test { Some("default".into()) ); } + + #[test] + fn subgraph_context_id() { + let selector = SubgraphSelector::ContextId { context_id: true }; + let context = crate::context::Context::new(); + let expected_id = context.id.clone(); + + // Test on_request + let request_result = selector.on_request( + &crate::services::SubgraphRequest::fake_builder() + .context(context.clone()) + .build(), + ); + assert_eq!(request_result, Some(expected_id.clone().into())); + + // Test on_response + let response_result = selector.on_response( + &crate::services::SubgraphResponse::fake_builder() + .context(context.clone()) + .build(), + ); + assert_eq!(response_result, Some(expected_id.clone().into())); + + // Test on_error + let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); + assert_eq!(error_result, Some(expected_id.into())); + + // Test that context_id: false returns None + let selector_disabled = SubgraphSelector::ContextId { context_id: false }; + assert_eq!( + selector_disabled.on_request( + &crate::services::SubgraphRequest::fake_builder() + .context(context) + .build(), + ), + None + ); + } } diff --git a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs index ad54a09c0d..585ccf07bd 100644 --- a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs @@ -212,6 +212,11 @@ pub(crate) enum SupergraphSelector { /// Boolean returning true if it's the primary response and not events like subscription events or deferred responses is_primary_response: bool, }, + /// The context ID of the request (unique per request). + ContextId { + /// The context ID + context_id: bool, + }, } impl Selector for SupergraphSelector { @@ -312,6 +317,9 @@ impl Selector for SupergraphSelector { } SupergraphSelector::Static(val) => Some(val.clone().into()), SupergraphSelector::StaticField { r#static } => Some(r#static.clone().into()), + SupergraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(request.context.id.clone())) + } // For response _ => None, } @@ -406,6 +414,9 @@ impl Selector for SupergraphSelector { } if *is_primary => Some(true.into()), SupergraphSelector::Static(val) => Some(val.clone().into()), SupergraphSelector::StaticField { r#static } => Some(r#static.clone().into()), + SupergraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(response.context.id.clone())) + } // For request _ => None, } @@ -508,6 +519,9 @@ impl Selector for SupergraphSelector { .or_else(|| default.maybe_to_otel_value()), SupergraphSelector::Static(val) => Some(val.clone().into()), SupergraphSelector::StaticField { r#static } => Some(r#static.clone().into()), + SupergraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(ctx.id.clone())) + } _ => None, } } @@ -573,6 +587,9 @@ impl Selector for SupergraphSelector { ctx.get_json_value(FIRST_EVENT_CONTEXT_KEY) == Some(serde_json_bytes::Value::Bool(true)), )), + SupergraphSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(ctx.id.clone())) + } _ => None, } } @@ -599,6 +616,7 @@ impl Selector for SupergraphSelector { | SupergraphSelector::Env { .. } | SupergraphSelector::Static(_) | SupergraphSelector::StaticField { .. } + | SupergraphSelector::ContextId { .. } ), Stage::Response => matches!( self, @@ -612,6 +630,7 @@ impl Selector for SupergraphSelector { | SupergraphSelector::IsPrimaryResponse { .. } | SupergraphSelector::Static(_) | SupergraphSelector::StaticField { .. } + | SupergraphSelector::ContextId { .. } ), Stage::ResponseEvent => matches!( self, @@ -625,6 +644,7 @@ impl Selector for SupergraphSelector { | SupergraphSelector::ResponseContext { .. } | SupergraphSelector::Static(_) | SupergraphSelector::StaticField { .. } + | SupergraphSelector::ContextId { .. } ), Stage::ResponseField => false, Stage::Error => matches!( @@ -637,6 +657,7 @@ impl Selector for SupergraphSelector { | SupergraphSelector::StaticField { .. } | SupergraphSelector::ResponseContext { .. } | SupergraphSelector::IsPrimaryResponse { .. } + | SupergraphSelector::ContextId { .. } ), Stage::Drop => matches!( self, @@ -1199,4 +1220,50 @@ mod test { Some("default".into()) ); } + + #[test] + fn supergraph_context_id() { + let selector = SupergraphSelector::ContextId { context_id: true }; + let context = crate::context::Context::new(); + let expected_id = context.id.clone(); + + // Test on_request + let request_result = selector.on_request( + &crate::services::SupergraphRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ); + assert_eq!(request_result, Some(expected_id.clone().into())); + + // Test on_response + let response_result = selector.on_response( + &crate::services::SupergraphResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ); + assert_eq!(response_result, Some(expected_id.clone().into())); + + // Test on_response_event + let event_result = + selector.on_response_event(&crate::graphql::Response::builder().build(), &context); + assert_eq!(event_result, Some(expected_id.clone().into())); + + // Test on_error + let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); + assert_eq!(error_result, Some(expected_id.into())); + + // Test that context_id: false returns None + let selector_disabled = SupergraphSelector::ContextId { context_id: false }; + assert_eq!( + selector_disabled.on_request( + &crate::services::SupergraphRequest::fake_builder() + .context(context) + .build() + .unwrap(), + ), + None + ); + } } From 884805c93dfe4e0a1b45a8e10a2b959132dd99c0 Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:35:09 -0500 Subject: [PATCH 2/9] Updating snapshot --- ...nfiguration__tests__schema_generation.snap | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 29b0c56665..64f2a4762a 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -8617,6 +8617,20 @@ expression: "&schema" "trace_id" ], "type": "object" + }, + { + "additionalProperties": false, + "description": "The context ID of the request (unique per request).", + "properties": { + "context_id": { + "description": "The context ID", + "type": "boolean" + } + }, + "required": [ + "context_id" + ], + "type": "object" } ] }, @@ -10480,6 +10494,20 @@ expression: "&schema" "response_cache_control" ], "type": "object" + }, + { + "additionalProperties": false, + "description": "The context ID of the request (unique per request).", + "properties": { + "context_id": { + "description": "The context ID", + "type": "boolean" + } + }, + "required": [ + "context_id" + ], + "type": "object" } ] }, @@ -11412,6 +11440,20 @@ expression: "&schema" "is_primary_response" ], "type": "object" + }, + { + "additionalProperties": false, + "description": "The context ID of the request (unique per request).", + "properties": { + "context_id": { + "description": "The context ID", + "type": "boolean" + } + }, + "required": [ + "context_id" + ], + "type": "object" } ] }, From 34b4e3d834f8e91ad8761b38c35b2672840543fc Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Thu, 19 Feb 2026 16:42:25 -0500 Subject: [PATCH 3/9] Adding Changeset --- .changesets/feat_context_id_selector.md | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .changesets/feat_context_id_selector.md diff --git a/.changesets/feat_context_id_selector.md b/.changesets/feat_context_id_selector.md new file mode 100644 index 0000000000..676d54c08e --- /dev/null +++ b/.changesets/feat_context_id_selector.md @@ -0,0 +1,27 @@ +### Add `context_id` selector for telemetry to expose unique per-request identifier ([GRAPHOS-67](https://apollographql.atlassian.net/browse/GRAPHOS-67)) + +A new `context_id` selector is now available for router, supergraph, and subgraph telemetry instrumentation. This selector exposes the unique per-request context ID that can be used to reliably correlate and debug requests in traces, logs, and custom events. + +Previously, the context ID was only accessible via Rhai scripts as `request.id`, but Rhai runs after telemetry, preventing its use in telemetry attributes. With this change, users can now include the context ID in spans and other telemetry data. + +Example configuration: + +```yaml +telemetry: + instrumentation: + spans: + router: + attributes: + "request.id": + context_id: true + supergraph: + attributes: + "request.id": + context_id: true + subgraph: + attributes: + "request.id": + context_id: true +``` + +By [@BobaFetters](https://github.com/BobaFetters) in https://github.com/apollographql/router/pull/8899 From 9d6d6911328bbdc566a269413f7c6de94cb45fc3 Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Wed, 25 Feb 2026 03:08:44 -0500 Subject: [PATCH 4/9] Adding context id to connectors --- .changesets/feat_context_id_selector.md | 6 +- ...nfiguration__tests__schema_generation.snap | 14 +++++ .../src/plugin/test/mock/connector.rs | 16 ++++- .../plugins/connectors/handle_responses.rs | 2 + .../src/plugins/coprocessor/connector.rs | 1 + apollo-router/src/plugins/coprocessor/test.rs | 2 + apollo-router/src/plugins/headers/mod.rs | 1 + .../telemetry/config_new/connector/events.rs | 2 + .../config_new/connector/selectors.rs | 59 +++++++++++++++++++ .../telemetry/config_new/instruments.rs | 1 + .../src/plugins/telemetry/fmt_layer.rs | 2 + .../src/plugins/traffic_shaping/mod.rs | 8 ++- .../src/services/connector/request_service.rs | 7 +++ 13 files changed, 115 insertions(+), 6 deletions(-) diff --git a/.changesets/feat_context_id_selector.md b/.changesets/feat_context_id_selector.md index 676d54c08e..e0bd02e11c 100644 --- a/.changesets/feat_context_id_selector.md +++ b/.changesets/feat_context_id_selector.md @@ -1,6 +1,6 @@ ### Add `context_id` selector for telemetry to expose unique per-request identifier ([GRAPHOS-67](https://apollographql.atlassian.net/browse/GRAPHOS-67)) -A new `context_id` selector is now available for router, supergraph, and subgraph telemetry instrumentation. This selector exposes the unique per-request context ID that can be used to reliably correlate and debug requests in traces, logs, and custom events. +A new `context_id` selector is now available for router, supergraph, subgraph, and connector telemetry instrumentation. This selector exposes the unique per-request context ID that can be used to reliably correlate and debug requests in traces, logs, and custom events. Previously, the context ID was only accessible via Rhai scripts as `request.id`, but Rhai runs after telemetry, preventing its use in telemetry attributes. With this change, users can now include the context ID in spans and other telemetry data. @@ -22,6 +22,10 @@ telemetry: attributes: "request.id": context_id: true + connector: + attributes: + "request.id": + context_id: true ``` By [@BobaFetters](https://github.com/BobaFetters) in https://github.com/apollographql/router/pull/8899 diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 5e8920b7cf..8248abc663 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -2543,6 +2543,20 @@ expression: "&schema" "connector_on_response_error" ], "type": "object" + }, + { + "additionalProperties": false, + "description": "The context ID of the request (unique per request).", + "properties": { + "context_id": { + "description": "The context ID", + "type": "boolean" + } + }, + "required": [ + "context_id" + ], + "type": "object" } ] }, diff --git a/apollo-router/src/plugin/test/mock/connector.rs b/apollo-router/src/plugin/test/mock/connector.rs index e3f58de8e7..e9de1f4f91 100644 --- a/apollo-router/src/plugin/test/mock/connector.rs +++ b/apollo-router/src/plugin/test/mock/connector.rs @@ -112,7 +112,13 @@ impl Service for MockConnector { let data = json!(response); let headers = self.headers.clone(); - ConnectorResponse::test_new(response_key, Default::default(), data, Some(headers)) + ConnectorResponse::test_new( + req.context.clone(), + response_key, + vec![], + data, + Some(headers), + ) } else { let error_message = format!( "couldn't find mock for query {}", @@ -122,7 +128,13 @@ impl Service for MockConnector { let data = json!(error_message); let headers = self.headers.clone(); - ConnectorResponse::test_new(response_key, Default::default(), data, Some(headers)) + ConnectorResponse::test_new( + req.context.clone(), + response_key, + vec![], + data, + Some(headers), + ) }; future::ok(response) } diff --git a/apollo-router/src/plugins/connectors/handle_responses.rs b/apollo-router/src/plugins/connectors/handle_responses.rs index 50dc063a8e..13df040284 100644 --- a/apollo-router/src/plugins/connectors/handle_responses.rs +++ b/apollo-router/src/plugins/connectors/handle_responses.rs @@ -199,6 +199,7 @@ pub(crate) async fn process_response( } connector::request_service::Response { + context: context.clone(), transport_result: result, mapped_response, } @@ -266,6 +267,7 @@ fn log_connectors_event( // connectors events. let response = connector::request_service::Response { + context: context.clone(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: parts.clone(), })), diff --git a/apollo-router/src/plugins/coprocessor/connector.rs b/apollo-router/src/plugins/coprocessor/connector.rs index e277170b55..7650bef6ff 100644 --- a/apollo-router/src/plugins/coprocessor/connector.rs +++ b/apollo-router/src/plugins/coprocessor/connector.rs @@ -326,6 +326,7 @@ where } let res = request_service::Response { + context: request.context.clone(), transport_result: Err(ConnectorError::TransportFailure(message)), mapped_response: MappedResponse::Error { error: runtime_error, diff --git a/apollo-router/src/plugins/coprocessor/test.rs b/apollo-router/src/plugins/coprocessor/test.rs index e240d8beae..6eebf3d735 100644 --- a/apollo-router/src/plugins/coprocessor/test.rs +++ b/apollo-router/src/plugins/coprocessor/test.rs @@ -5522,6 +5522,7 @@ mod tests { .collect(); let response = request_service::Response::test_new( + req.context.clone(), req.key, Default::default(), serde_json_bytes::json!("ok"), @@ -6136,6 +6137,7 @@ mod tests { { tower::service_fn(|_req: request_service::Request| async { Ok(request_service::Response { + context: _req.context, transport_result: Err( apollo_federation::connectors::runtime::errors::Error::TransportFailure( "original error".to_string(), diff --git a/apollo-router/src/plugins/headers/mod.rs b/apollo-router/src/plugins/headers/mod.rs index 1af6656392..18a2af6e0e 100644 --- a/apollo-router/src/plugins/headers/mod.rs +++ b/apollo-router/src/plugins/headers/mod.rs @@ -1588,6 +1588,7 @@ mod test { selection: Arc::new(JSONSelection::parse("$.data").unwrap()), }; Ok(connector::request_service::Response::test_new( + _req.context.clone(), key, Vec::new(), json!(""), diff --git a/apollo-router/src/plugins/telemetry/config_new/connector/events.rs b/apollo-router/src/plugins/telemetry/config_new/connector/events.rs index de29e4d2cc..2fb89dc8ef 100644 --- a/apollo-router/src/plugins/telemetry/config_new/connector/events.rs +++ b/apollo-router/src/plugins/telemetry/config_new/connector/events.rs @@ -215,6 +215,7 @@ mod tests { }; test_harness .call_connector_request_service(connector_request, |request| Response { + context: request.context.clone(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(200) @@ -301,6 +302,7 @@ mod tests { }; test_harness .call_connector_request_service(connector_request, |request| Response { + context: request.context.clone(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(200) diff --git a/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs index 7df0a88b8c..7829bc0572 100644 --- a/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs @@ -151,6 +151,11 @@ pub(crate) enum ConnectorSelector { /// set, returns true when the response contains a non-200 status code connector_on_response_error: bool, }, + /// The context ID of the request (unique per request). + ContextId { + /// The context ID + context_id: bool, + }, } impl Selector for ConnectorSelector { @@ -249,6 +254,9 @@ impl Selector for ConnectorSelector { .ok() .flatten() .map(opentelemetry::Value::from), + ConnectorSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(request.context.id.clone())) + } _ => None, } } @@ -314,6 +322,9 @@ impl Selector for ConnectorSelector { } if *connector_on_response_error => { Some(matches!(response.mapped_response, MappedResponse::Error { .. }).into()) } + ConnectorSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(response.context.id.clone())) + } _ => None, } } @@ -322,6 +333,9 @@ impl Selector for ConnectorSelector { match self { ConnectorSelector::Error { .. } => Some(error.to_string().into()), ConnectorSelector::StaticField { r#static } => Some(r#static.clone().into()), + ConnectorSelector::ContextId { context_id } if *context_id => { + Some(opentelemetry::Value::from(_ctx.id.clone())) + } _ => None, } } @@ -347,6 +361,7 @@ impl Selector for ConnectorSelector { | ConnectorSelector::RequestContext { .. } | ConnectorSelector::SupergraphOperationName { .. } | ConnectorSelector::SupergraphOperationKind { .. } + | ConnectorSelector::ContextId { .. } ), Stage::Response => matches!( self, @@ -359,6 +374,7 @@ impl Selector for ConnectorSelector { | ConnectorSelector::StaticField { .. } | ConnectorSelector::ResponseMappingProblems { .. } | ConnectorSelector::OnResponseError { .. } + | ConnectorSelector::ContextId { .. } ), Stage::ResponseEvent => false, Stage::ResponseField => false, @@ -370,6 +386,7 @@ impl Selector for ConnectorSelector { | ConnectorSelector::ConnectorHttpMethod { .. } | ConnectorSelector::ConnectorUrlTemplate { .. } | ConnectorSelector::StaticField { .. } + | ConnectorSelector::ContextId { .. } ), Stage::Drop => matches!(self, ConnectorSelector::StaticField { .. }), } @@ -403,6 +420,7 @@ mod tests { use opentelemetry::Array; use opentelemetry::StringValue; use opentelemetry::Value; + use tower::BoxError; use super::ConnectorSelector; use super::ConnectorSource; @@ -506,6 +524,7 @@ mod tests { mapping_problems: Vec, ) -> Response { Response { + context: Context::new(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(status_code) @@ -526,6 +545,7 @@ mod tests { fn connector_response_with_mapped_error(status_code: StatusCode) -> Response { Response { + context: Context::new(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(status_code) @@ -544,6 +564,7 @@ mod tests { fn connector_response_with_header() -> Response { Response { + context: Context::new(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(200) @@ -978,4 +999,42 @@ mod tests { Value::String("invalid digit found in string".into()) ); } + + #[test] + fn connector_context_id() { + let selector = ConnectorSelector::ContextId { context_id: true }; + let context = Context::new(); + let expected_id = context.id.clone(); + + // Test on_request + assert_eq!( + selector.on_request(&connector_request( + http_request(), + Some(context.clone()), + None + )), + Some(expected_id.clone().into()) + ); + + // Test on_response + let mut response = connector_response(StatusCode::OK); + response.context = context.clone(); + assert_eq!( + selector.on_response(&response), + Some(expected_id.clone().into()) + ); + + // Test on_error + assert_eq!( + selector.on_error(&BoxError::from("test error".to_string()), &context), + Some(expected_id.into()) + ); + + // Test that context_id: false returns None + let selector_disabled = ConnectorSelector::ContextId { context_id: false }; + assert_eq!( + selector_disabled.on_request(&connector_request(http_request(), Some(context), None)), + None + ); + } } diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 5edca01a3d..19c34ff440 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -3247,6 +3247,7 @@ mod tests { .unwrap(); *http_response.headers_mut() = convert_http_headers(headers); let response = Response { + context: context.clone(), transport_result: Ok(TransportResponse::Http( HttpResponse { inner: http_response.into_parts().0, diff --git a/apollo-router/src/plugins/telemetry/fmt_layer.rs b/apollo-router/src/plugins/telemetry/fmt_layer.rs index c791568df7..449fff4a9d 100644 --- a/apollo-router/src/plugins/telemetry/fmt_layer.rs +++ b/apollo-router/src/plugins/telemetry/fmt_layer.rs @@ -885,6 +885,7 @@ connector: connector_events.on_request(&connector_request); let connector_response = Response { + context: context.clone(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(200) @@ -1244,6 +1245,7 @@ subgraph: connector_events.on_request(&connector_request); let connector_response = Response { + context: context.clone(), transport_result: Ok(TransportResponse::Http(HttpResponse { inner: http::Response::builder() .status(200) diff --git a/apollo-router/src/plugins/traffic_shaping/mod.rs b/apollo-router/src/plugins/traffic_shaping/mod.rs index 8ecef81504..5074c82219 100644 --- a/apollo-router/src/plugins/traffic_shaping/mod.rs +++ b/apollo-router/src/plugins/traffic_shaping/mod.rs @@ -445,22 +445,24 @@ impl PluginPrivate for TrafficShaping { ServiceBuilder::new() .map_future_with_request_data( - |req: &Request| req.key.clone(), - move |response_key, future| { + |req: &Request| (req.context.clone(), req.key.clone()), + move |(context, response_key), future| { async { let response: Result = future.await; match response { Ok(ok) => Ok(ok), Err(err) if err.is::() => { let response = Response::error_new( + context, Error::GatewayTimeout, "Your request has been timed out", response_key, ); Ok(response) } - Err(err) if err.is::() => { + Err(err) if err.is::() => { let response = Response::error_new( + context, Error::RateLimited, "Your request has been rate limited", response_key, diff --git a/apollo-router/src/services/connector/request_service.rs b/apollo-router/src/services/connector/request_service.rs index 270ac38a90..3ad1a87290 100644 --- a/apollo-router/src/services/connector/request_service.rs +++ b/apollo-router/src/services/connector/request_service.rs @@ -85,6 +85,9 @@ pub struct Request { /// Response type for a connector #[derive(Debug)] pub struct Response { + /// The request context + pub(crate) context: Context, + /// The result of the transport request pub(crate) transport_result: Result, @@ -94,6 +97,7 @@ pub struct Response { impl Response { pub(crate) fn error_new( + context: Context, error: Error, message: impl Into, response_key: ResponseKey, @@ -107,6 +111,7 @@ impl Response { }; Self { + context, transport_result: Err(error), mapped_response, } @@ -114,6 +119,7 @@ impl Response { #[cfg(test)] pub(crate) fn test_new( + context: Context, response_key: ResponseKey, problems: Vec, data: serde_json_bytes::Value, @@ -135,6 +141,7 @@ impl Response { let http_response = HttpResponse { inner: parts }; Self { + context, transport_result: Ok(http_response.into()), mapped_response, } From 38b7980305462b97f176e3e9605e0a821343e235 Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Tue, 3 Mar 2026 02:17:35 -0500 Subject: [PATCH 5/9] Addressing feedback Making some style changes based on suggestions in the PR --- .changesets/feat_context_id_selector.md | 2 +- apollo-router/src/plugins/coprocessor/test.rs | 4 +- apollo-router/src/plugins/headers/mod.rs | 4 +- .../config_new/connector/selectors.rs | 38 +++++------ .../telemetry/config_new/router/selectors.rs | 55 ++++++++++------ .../config_new/subgraph/selectors.rs | 53 +++++++++------ .../config_new/supergraph/selectors.rs | 64 ++++++++++++------- 7 files changed, 132 insertions(+), 88 deletions(-) diff --git a/.changesets/feat_context_id_selector.md b/.changesets/feat_context_id_selector.md index e0bd02e11c..1328e95fa3 100644 --- a/.changesets/feat_context_id_selector.md +++ b/.changesets/feat_context_id_selector.md @@ -1,4 +1,4 @@ -### Add `context_id` selector for telemetry to expose unique per-request identifier ([GRAPHOS-67](https://apollographql.atlassian.net/browse/GRAPHOS-67)) +### Add `context_id` selector for telemetry to expose unique per-request identifier ([#8899](https://github.com/apollographql/router/pull/8899)) A new `context_id` selector is now available for router, supergraph, subgraph, and connector telemetry instrumentation. This selector exposes the unique per-request context ID that can be used to reliably correlate and debug requests in traces, logs, and custom events. diff --git a/apollo-router/src/plugins/coprocessor/test.rs b/apollo-router/src/plugins/coprocessor/test.rs index d5da408b30..220425b423 100644 --- a/apollo-router/src/plugins/coprocessor/test.rs +++ b/apollo-router/src/plugins/coprocessor/test.rs @@ -6167,9 +6167,9 @@ mod tests { fn create_error_connector_service() -> tower::util::BoxService { - tower::service_fn(|_req: request_service::Request| async { + tower::service_fn(|req: request_service::Request| async { Ok(request_service::Response { - context: _req.context, + context: req.context, transport_result: Err( apollo_federation::connectors::runtime::errors::Error::TransportFailure( "original error".to_string(), diff --git a/apollo-router/src/plugins/headers/mod.rs b/apollo-router/src/plugins/headers/mod.rs index 18a2af6e0e..a3ffce6ab9 100644 --- a/apollo-router/src/plugins/headers/mod.rs +++ b/apollo-router/src/plugins/headers/mod.rs @@ -1580,7 +1580,7 @@ mod test { } fn example_connector_response( - _req: connector::request_service::Request, + req: connector::request_service::Request, ) -> Result { let key = ResponseKey::RootField { name: "hello".to_string(), @@ -1588,7 +1588,7 @@ mod test { selection: Arc::new(JSONSelection::parse("$.data").unwrap()), }; Ok(connector::request_service::Response::test_new( - _req.context.clone(), + req.context.clone(), key, Vec::new(), json!(""), diff --git a/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs index 7829bc0572..79ee8ca870 100644 --- a/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/connector/selectors.rs @@ -329,12 +329,12 @@ impl Selector for ConnectorSelector { } } - fn on_error(&self, error: &BoxError, _ctx: &Context) -> Option { + fn on_error(&self, error: &BoxError, ctx: &Context) -> Option { match self { ConnectorSelector::Error { .. } => Some(error.to_string().into()), ConnectorSelector::StaticField { r#static } => Some(r#static.clone().into()), ConnectorSelector::ContextId { context_id } if *context_id => { - Some(opentelemetry::Value::from(_ctx.id.clone())) + Some(opentelemetry::Value::from(ctx.id.clone())) } _ => None, } @@ -1004,37 +1004,39 @@ mod tests { fn connector_context_id() { let selector = ConnectorSelector::ContextId { context_id: true }; let context = Context::new(); - let expected_id = context.id.clone(); + let expected_id: Value = context.id.clone().into(); // Test on_request assert_eq!( - selector.on_request(&connector_request( - http_request(), - Some(context.clone()), - None - )), - Some(expected_id.clone().into()) + selector + .on_request(&connector_request( + http_request(), + Some(context.clone()), + None + )) + .unwrap(), + expected_id ); // Test on_response let mut response = connector_response(StatusCode::OK); response.context = context.clone(); - assert_eq!( - selector.on_response(&response), - Some(expected_id.clone().into()) - ); + assert_eq!(selector.on_response(&response).unwrap(), expected_id); // Test on_error assert_eq!( - selector.on_error(&BoxError::from("test error".to_string()), &context), - Some(expected_id.into()) + selector + .on_error(&BoxError::from("test error".to_string()), &context) + .unwrap(), + expected_id ); // Test that context_id: false returns None let selector_disabled = ConnectorSelector::ContextId { context_id: false }; - assert_eq!( - selector_disabled.on_request(&connector_request(http_request(), Some(context), None)), - None + assert!( + selector_disabled + .on_request(&connector_request(http_request(), Some(context), None)) + .is_none() ); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index 59e15e2f93..cc490455fa 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -1070,40 +1070,53 @@ mod test { fn router_context_id() { let selector = RouterSelector::ContextId { context_id: true }; let context = crate::context::Context::new(); - let expected_id = context.id.clone(); + let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - let request_result = selector.on_request( - &crate::services::RouterRequest::fake_builder() - .context(context.clone()) - .build() + assert_eq!( + selector + .on_request( + &crate::services::RouterRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ) .unwrap(), + expected_id ); - assert_eq!(request_result, Some(expected_id.clone().into())); // Test on_response - let response_result = selector.on_response( - &crate::services::RouterResponse::fake_builder() - .context(context.clone()) - .build() + assert_eq!( + selector + .on_response( + &crate::services::RouterResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ) .unwrap(), + expected_id ); - assert_eq!(response_result, Some(expected_id.clone().into())); // Test on_error - let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); - assert_eq!(error_result, Some(expected_id.into())); + assert_eq!( + selector + .on_error(&BoxError::from(String::from("test error")), &context) + .unwrap(), + expected_id + ); // Test that context_id: false returns None let selector_disabled = RouterSelector::ContextId { context_id: false }; - assert_eq!( - selector_disabled.on_request( - &crate::services::RouterRequest::fake_builder() - .context(context) - .build() - .unwrap(), - ), - None + assert!( + selector_disabled + .on_request( + &crate::services::RouterRequest::fake_builder() + .context(context) + .build() + .unwrap(), + ) + .is_none() ); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs index 8d65f55cbc..3665c4f45e 100644 --- a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs @@ -2196,37 +2196,50 @@ mod test { fn subgraph_context_id() { let selector = SubgraphSelector::ContextId { context_id: true }; let context = crate::context::Context::new(); - let expected_id = context.id.clone(); + let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - let request_result = selector.on_request( - &crate::services::SubgraphRequest::fake_builder() - .context(context.clone()) - .build(), + assert_eq!( + selector + .on_request( + &crate::services::SubgraphRequest::fake_builder() + .context(context.clone()) + .build(), + ) + .unwrap(), + expected_id ); - assert_eq!(request_result, Some(expected_id.clone().into())); // Test on_response - let response_result = selector.on_response( - &crate::services::SubgraphResponse::fake_builder() - .context(context.clone()) - .build(), + assert_eq!( + selector + .on_response( + &crate::services::SubgraphResponse::fake_builder() + .context(context.clone()) + .build(), + ) + .unwrap(), + expected_id ); - assert_eq!(response_result, Some(expected_id.clone().into())); // Test on_error - let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); - assert_eq!(error_result, Some(expected_id.into())); + assert_eq!( + selector + .on_error(&BoxError::from(String::from("test error")), &context) + .unwrap(), + expected_id + ); // Test that context_id: false returns None let selector_disabled = SubgraphSelector::ContextId { context_id: false }; - assert_eq!( - selector_disabled.on_request( - &crate::services::SubgraphRequest::fake_builder() - .context(context) - .build(), - ), - None + assert!( + selector_disabled + .on_request( + &crate::services::SubgraphRequest::fake_builder() + .context(context) + .build(), + ) + .is_none() ); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs index 585ccf07bd..060d019d18 100644 --- a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs @@ -1225,45 +1225,61 @@ mod test { fn supergraph_context_id() { let selector = SupergraphSelector::ContextId { context_id: true }; let context = crate::context::Context::new(); - let expected_id = context.id.clone(); + let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - let request_result = selector.on_request( - &crate::services::SupergraphRequest::fake_builder() - .context(context.clone()) - .build() + assert_eq!( + selector + .on_request( + &crate::services::SupergraphRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ) .unwrap(), + expected_id ); - assert_eq!(request_result, Some(expected_id.clone().into())); // Test on_response - let response_result = selector.on_response( - &crate::services::SupergraphResponse::fake_builder() - .context(context.clone()) - .build() + assert_eq!( + selector + .on_response( + &crate::services::SupergraphResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(), + ) .unwrap(), + expected_id ); - assert_eq!(response_result, Some(expected_id.clone().into())); // Test on_response_event - let event_result = - selector.on_response_event(&crate::graphql::Response::builder().build(), &context); - assert_eq!(event_result, Some(expected_id.clone().into())); + assert_eq!( + selector + .on_response_event(&crate::graphql::Response::builder().build(), &context) + .unwrap(), + expected_id + ); // Test on_error - let error_result = selector.on_error(&BoxError::from(String::from("test error")), &context); - assert_eq!(error_result, Some(expected_id.into())); + assert_eq!( + selector + .on_error(&BoxError::from(String::from("test error")), &context) + .unwrap(), + expected_id + ); // Test that context_id: false returns None let selector_disabled = SupergraphSelector::ContextId { context_id: false }; - assert_eq!( - selector_disabled.on_request( - &crate::services::SupergraphRequest::fake_builder() - .context(context) - .build() - .unwrap(), - ), - None + assert!( + selector_disabled + .on_request( + &crate::services::SupergraphRequest::fake_builder() + .context(context) + .build() + .unwrap(), + ) + .is_none() ); } } From 98fe9b678d77c61ab9d9b8e8d3e2c762820db05d Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Tue, 3 Mar 2026 02:25:28 -0500 Subject: [PATCH 6/9] Changeset update --- .changesets/feat_context_id_selector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changesets/feat_context_id_selector.md b/.changesets/feat_context_id_selector.md index 1328e95fa3..5816518932 100644 --- a/.changesets/feat_context_id_selector.md +++ b/.changesets/feat_context_id_selector.md @@ -2,7 +2,7 @@ A new `context_id` selector is now available for router, supergraph, subgraph, and connector telemetry instrumentation. This selector exposes the unique per-request context ID that can be used to reliably correlate and debug requests in traces, logs, and custom events. -Previously, the context ID was only accessible via Rhai scripts as `request.id`, but Rhai runs after telemetry, preventing its use in telemetry attributes. With this change, users can now include the context ID in spans and other telemetry data. +Previously, while the context ID was accessible in Rhai scripts as `request.id`, there was no telemetry selector to expose it. With this change, users can now include `context_id: true` in their telemetry configuration to add the context ID to spans, logs, and custom events. Example configuration: From ea5ac9fe01d980b2b300a282c687b0a75304d445 Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Thu, 12 Mar 2026 11:52:33 -0400 Subject: [PATCH 7/9] Address minor style change for readability --- .../telemetry/config_new/router/selectors.rs | 49 +++++++------------ .../config_new/subgraph/selectors.rs | 43 ++++++---------- .../config_new/supergraph/selectors.rs | 49 +++++++------------ 3 files changed, 48 insertions(+), 93 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index cc490455fa..7131d57f53 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -544,6 +544,8 @@ mod test { use crate::plugins::telemetry::config_new::selectors::ResponseStatus; use crate::plugins::telemetry::otel; use crate::query_planner::APOLLO_OPERATION_ID; + use crate::services::RouterRequest; + use crate::services::RouterResponse; #[test] fn router_static() { @@ -1073,30 +1075,18 @@ mod test { let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - assert_eq!( - selector - .on_request( - &crate::services::RouterRequest::fake_builder() - .context(context.clone()) - .build() - .unwrap(), - ) - .unwrap(), - expected_id - ); + let request = RouterRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(); + assert_eq!(selector.on_request(&request).unwrap(), expected_id); // Test on_response - assert_eq!( - selector - .on_response( - &crate::services::RouterResponse::fake_builder() - .context(context.clone()) - .build() - .unwrap(), - ) - .unwrap(), - expected_id - ); + let response = RouterResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(); + assert_eq!(selector.on_response(&response).unwrap(), expected_id); // Test on_error assert_eq!( @@ -1108,15 +1098,10 @@ mod test { // Test that context_id: false returns None let selector_disabled = RouterSelector::ContextId { context_id: false }; - assert!( - selector_disabled - .on_request( - &crate::services::RouterRequest::fake_builder() - .context(context) - .build() - .unwrap(), - ) - .is_none() - ); + let request = RouterRequest::fake_builder() + .context(context) + .build() + .unwrap(); + assert!(selector_disabled.on_request(&request).is_none()); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs index 3665c4f45e..405b2f4abe 100644 --- a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs @@ -926,6 +926,8 @@ mod test { use crate::plugins::telemetry::config_new::subgraph::selectors::SubgraphRequestBodySize; use crate::plugins::telemetry::config_new::subgraph::selectors::SubgraphSelector; use crate::plugins::telemetry::otel; + use crate::services::SubgraphRequest; + use crate::services::SubgraphResponse; use crate::services::subgraph::SubgraphRequestId; #[test] @@ -2199,28 +2201,16 @@ mod test { let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - assert_eq!( - selector - .on_request( - &crate::services::SubgraphRequest::fake_builder() - .context(context.clone()) - .build(), - ) - .unwrap(), - expected_id - ); + let request = SubgraphRequest::fake_builder() + .context(context.clone()) + .build(); + assert_eq!(selector.on_request(&request).unwrap(), expected_id); // Test on_response - assert_eq!( - selector - .on_response( - &crate::services::SubgraphResponse::fake_builder() - .context(context.clone()) - .build(), - ) - .unwrap(), - expected_id - ); + let response = SubgraphResponse::fake_builder() + .context(context.clone()) + .build(); + assert_eq!(selector.on_response(&response).unwrap(), expected_id); // Test on_error assert_eq!( @@ -2232,14 +2222,9 @@ mod test { // Test that context_id: false returns None let selector_disabled = SubgraphSelector::ContextId { context_id: false }; - assert!( - selector_disabled - .on_request( - &crate::services::SubgraphRequest::fake_builder() - .context(context) - .build(), - ) - .is_none() - ); + let request = SubgraphRequest::fake_builder() + .context(context) + .build(); + assert!(selector_disabled.on_request(&request).is_none()); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs index 060d019d18..4ea814a532 100644 --- a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs @@ -693,6 +693,8 @@ mod test { use crate::plugins::telemetry::config_new::supergraph::selectors::SupergraphSelector; use crate::plugins::telemetry::otel; use crate::services::FIRST_EVENT_CONTEXT_KEY; + use crate::services::SupergraphRequest; + use crate::services::SupergraphResponse; use crate::spec::operation_limits::OperationLimits; #[test] @@ -1228,30 +1230,18 @@ mod test { let expected_id: opentelemetry::Value = context.id.clone().into(); // Test on_request - assert_eq!( - selector - .on_request( - &crate::services::SupergraphRequest::fake_builder() - .context(context.clone()) - .build() - .unwrap(), - ) - .unwrap(), - expected_id - ); + let request = SupergraphRequest::fake_builder() + .context(context.clone()) + .build() + .unwrap(); + assert_eq!(selector.on_request(&request).unwrap(), expected_id); // Test on_response - assert_eq!( - selector - .on_response( - &crate::services::SupergraphResponse::fake_builder() - .context(context.clone()) - .build() - .unwrap(), - ) - .unwrap(), - expected_id - ); + let response = SupergraphResponse::fake_builder() + .context(context.clone()) + .build() + .unwrap(); + assert_eq!(selector.on_response(&response).unwrap(), expected_id); // Test on_response_event assert_eq!( @@ -1271,15 +1261,10 @@ mod test { // Test that context_id: false returns None let selector_disabled = SupergraphSelector::ContextId { context_id: false }; - assert!( - selector_disabled - .on_request( - &crate::services::SupergraphRequest::fake_builder() - .context(context) - .build() - .unwrap(), - ) - .is_none() - ); + let request = SupergraphRequest::fake_builder() + .context(context) + .build() + .unwrap(); + assert!(selector_disabled.on_request(&request).is_none()); } } From 3662380a6940cd0ae156b8d84757f2f6c3e8464c Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:47:52 -0400 Subject: [PATCH 8/9] Fixing linting issues --- .../src/plugins/telemetry/config_new/router/selectors.rs | 5 +---- .../src/plugins/telemetry/config_new/subgraph/selectors.rs | 4 +--- .../src/plugins/telemetry/config_new/supergraph/selectors.rs | 5 +---- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index 7131d57f53..e10631db60 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -1098,10 +1098,7 @@ mod test { // Test that context_id: false returns None let selector_disabled = RouterSelector::ContextId { context_id: false }; - let request = RouterRequest::fake_builder() - .context(context) - .build() - .unwrap(); + let request = RouterRequest::fake_builder().context(context).build().unwrap(); assert!(selector_disabled.on_request(&request).is_none()); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs index 405b2f4abe..f761f5db30 100644 --- a/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/subgraph/selectors.rs @@ -2222,9 +2222,7 @@ mod test { // Test that context_id: false returns None let selector_disabled = SubgraphSelector::ContextId { context_id: false }; - let request = SubgraphRequest::fake_builder() - .context(context) - .build(); + let request = SubgraphRequest::fake_builder().context(context).build(); assert!(selector_disabled.on_request(&request).is_none()); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs index 4ea814a532..95c0009797 100644 --- a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs @@ -1261,10 +1261,7 @@ mod test { // Test that context_id: false returns None let selector_disabled = SupergraphSelector::ContextId { context_id: false }; - let request = SupergraphRequest::fake_builder() - .context(context) - .build() - .unwrap(); + let request = SupergraphRequest::fake_builder().context(context).build().unwrap(); assert!(selector_disabled.on_request(&request).is_none()); } } From e0850aa121bcddd8ea4e2eb921d4b5e6eb182bba Mon Sep 17 00:00:00 2001 From: Zach FettersMoore <4425109+BobaFetters@users.noreply.github.com> Date: Thu, 12 Mar 2026 15:00:48 -0400 Subject: [PATCH 9/9] More linting updates --- .../src/plugins/telemetry/config_new/router/selectors.rs | 5 ++++- .../src/plugins/telemetry/config_new/supergraph/selectors.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index e10631db60..7131d57f53 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -1098,7 +1098,10 @@ mod test { // Test that context_id: false returns None let selector_disabled = RouterSelector::ContextId { context_id: false }; - let request = RouterRequest::fake_builder().context(context).build().unwrap(); + let request = RouterRequest::fake_builder() + .context(context) + .build() + .unwrap(); assert!(selector_disabled.on_request(&request).is_none()); } } diff --git a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs index 95c0009797..4ea814a532 100644 --- a/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/supergraph/selectors.rs @@ -1261,7 +1261,10 @@ mod test { // Test that context_id: false returns None let selector_disabled = SupergraphSelector::ContextId { context_id: false }; - let request = SupergraphRequest::fake_builder().context(context).build().unwrap(); + let request = SupergraphRequest::fake_builder() + .context(context) + .build() + .unwrap(); assert!(selector_disabled.on_request(&request).is_none()); } }