diff --git a/.changesets/config_http_client_header_telemetry.md b/.changesets/config_http_client_header_telemetry.md new file mode 100644 index 0000000000..3616e33394 --- /dev/null +++ b/.changesets/config_http_client_header_telemetry.md @@ -0,0 +1,20 @@ +### Telemetry instrumentation config for http_client headers ([PR #8349](https://github.com/apollographql/router/pull/8349)) + +Adds a new telemetry instrumentation configuration for the http_client spans. This setting allows request headers added by Rhai scripts to be attached to the http_client span. The `some_rhai_response_header` value is available on the subgraph span as before. + +```yaml +telemetry: + instrumentation: + spans: + mode: spec_compliant + subgraph: + attributes: + http.response.header.some_rhai_response_header: + subgraph_response_header: "some_rhai_response_header" + http_client: + attributes: + http.request.header.some_rhai_request_header: + request_header: "some_rhai_request_header" +``` + +By [@bonnici](https://github.com/bonnici) in https://github.com/apollographql/router/pull/8349 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 dc85267663..3998910448 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 @@ -1157,6 +1157,133 @@ expression: "&schema" } ] }, + "ConditionHttpClientSelector": { + "description": "Specify a condition for when an [instrument][] should be mutated or an [event][] should be triggered.\n\n[instrument]: https://www.apollographql.com/docs/graphos/routing/observability/telemetry/instrumentation/instruments\n[event]: https://www.apollographql.com/docs/graphos/routing/observability/telemetry/instrumentation/events", + "oneOf": [ + { + "additionalProperties": false, + "description": "A condition to check a selection against a value.", + "properties": { + "eq": { + "items": { + "$ref": "#/definitions/HttpClientSelectorOrValue" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "eq" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "The first selection must be greater than the second selection.", + "properties": { + "gt": { + "items": { + "$ref": "#/definitions/HttpClientSelectorOrValue" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "gt" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "The first selection must be less than the second selection.", + "properties": { + "lt": { + "items": { + "$ref": "#/definitions/HttpClientSelectorOrValue" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "lt" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "A condition to check a selection against a selector.", + "properties": { + "exists": { + "$ref": "#/definitions/HttpClientSelector" + } + }, + "required": [ + "exists" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "All sub-conditions must be true.", + "properties": { + "all": { + "items": { + "$ref": "#/definitions/ConditionHttpClientSelector" + }, + "type": "array" + } + }, + "required": [ + "all" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "At least one sub-conditions must be true.", + "properties": { + "any": { + "items": { + "$ref": "#/definitions/ConditionHttpClientSelector" + }, + "type": "array" + } + }, + "required": [ + "any" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "The sub-condition must not be true", + "properties": { + "not": { + "$ref": "#/definitions/ConditionHttpClientSelector" + } + }, + "required": [ + "not" + ], + "type": "object" + }, + { + "const": "true", + "description": "Static true condition", + "type": "string" + }, + { + "const": "false", + "description": "Static false condition", + "type": "string" + } + ] + }, "ConditionRouterSelector": { "description": "Specify a condition for when an [instrument][] should be mutated or an [event][] should be triggered.\n\n[instrument]: https://www.apollographql.com/docs/graphos/routing/observability/telemetry/instrumentation/instruments\n[event]: https://www.apollographql.com/docs/graphos/routing/observability/telemetry/instrumentation/events", "oneOf": [ @@ -1553,6 +1680,21 @@ expression: "&schema" } ] }, + "ConditionalHttpClientSelector": { + "anyOf": [ + { + "$ref": "#/definitions/HttpClientSelector" + }, + { + "properties": { + "condition": { + "$ref": "#/definitions/ConditionHttpClientSelector" + } + }, + "type": "object" + } + ] + }, "ConditionalRouterSelector": { "anyOf": [ { @@ -3751,6 +3893,12 @@ expression: "&schema" }, "type": "object" }, + "ExtendedHttpClientAttributesWithConditionalHttpClientSelector": { + "additionalProperties": { + "$ref": "#/definitions/ConditionalHttpClientSelector" + }, + "type": "object" + }, "ExtendedRouterAttributesWithConditionalRouterSelector": { "additionalProperties": { "$ref": "#/definitions/ConditionalRouterSelector" @@ -5280,6 +5428,86 @@ expression: "&schema" } ] }, + "HttpClientSelector": { + "anyOf": [ + { + "additionalProperties": false, + "description": "A header from the HTTP request", + "properties": { + "default": { + "description": "Optional default value.", + "type": [ + "string", + "null" + ] + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "required": [ + "request_header" + ], + "type": "object" + }, + { + "additionalProperties": false, + "description": "A header from the HTTP response", + "properties": { + "default": { + "description": "Optional default value.", + "type": [ + "string", + "null" + ] + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "required": [ + "response_header" + ], + "type": "object" + } + ] + }, + "HttpClientSelectorOrValue": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/AttributeValue" + } + ], + "description": "A constant value." + }, + { + "allOf": [ + { + "$ref": "#/definitions/HttpClientSelector" + } + ], + "description": "Selector to extract a value from the pipeline." + } + ] + }, + "HttpClientSpans": { + "additionalProperties": false, + "properties": { + "attributes": { + "allOf": [ + { + "$ref": "#/definitions/ExtendedHttpClientAttributesWithConditionalHttpClientSelector" + } + ], + "description": "Custom attributes that are attached to the HTTP client span." + } + }, + "type": "object" + }, "HttpExporter": { "additionalProperties": false, "properties": { @@ -8279,6 +8507,14 @@ expression: "&schema" ], "description": "The attributes to include by default in spans based on their level as specified in the otel semantic conventions and Apollo documentation." }, + "http_client": { + "allOf": [ + { + "$ref": "#/definitions/HttpClientSpans" + } + ], + "description": "Attributes to include on the HTTP client span.\nHTTP client spans contain information about HTTP requests made to subgraphs, including any changes made by Rhai scripts." + }, "mode": { "allOf": [ { diff --git a/apollo-router/src/plugins/telemetry/config_new/http_client/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/http_client/attributes.rs new file mode 100644 index 0000000000..0fdfcf8d1d --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/http_client/attributes.rs @@ -0,0 +1,41 @@ +use std::fmt::Debug; + +use opentelemetry::KeyValue; +use schemars::JsonSchema; +use serde::Deserialize; +use tower::BoxError; + +use crate::Context; +use crate::plugins::telemetry::config_new::DefaultForLevel; +use crate::plugins::telemetry::config_new::Selectors; +use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; +use crate::plugins::telemetry::otlp::TelemetryDataKind; +use crate::services::http; + +#[derive(Deserialize, JsonSchema, Clone, Default, Debug)] +#[cfg_attr(test, derive(PartialEq))] +#[serde(deny_unknown_fields, default)] +pub(crate) struct HttpClientAttributes {} + +impl DefaultForLevel for HttpClientAttributes { + fn defaults_for_level( + &mut self, + _requirement_level: DefaultAttributeRequirementLevel, + _kind: TelemetryDataKind, + ) { + } +} + +impl Selectors for HttpClientAttributes { + fn on_request(&self, _request: &http::HttpRequest) -> Vec { + Vec::new() + } + + fn on_response(&self, _response: &http::HttpResponse) -> Vec { + Vec::new() + } + + fn on_error(&self, _error: &BoxError, _ctx: &Context) -> Vec { + Vec::new() + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/http_client/mod.rs b/apollo-router/src/plugins/telemetry/config_new/http_client/mod.rs new file mode 100644 index 0000000000..a8e021de87 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/http_client/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod attributes; +pub(crate) mod selectors; +pub(crate) mod spans; diff --git a/apollo-router/src/plugins/telemetry/config_new/http_client/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/http_client/selectors.rs new file mode 100644 index 0000000000..c0196a5d7e --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/http_client/selectors.rs @@ -0,0 +1,178 @@ +use derivative::Derivative; +use schemars::JsonSchema; +use serde::Deserialize; +use tower::BoxError; + +use crate::Context; +use crate::plugins::telemetry::config_new::Selector; +use crate::plugins::telemetry::config_new::Stage; +use crate::plugins::telemetry::config_new::instruments::InstrumentValue; +use crate::plugins::telemetry::config_new::instruments::Standard; +use crate::services::http; + +#[derive(Deserialize, JsonSchema, Clone, Debug)] +#[serde(deny_unknown_fields, rename_all = "snake_case", untagged)] +pub(crate) enum HttpClientValue { + Standard(Standard), + Custom(HttpClientSelector), +} + +impl From<&HttpClientValue> for InstrumentValue { + fn from(value: &HttpClientValue) -> Self { + match value { + HttpClientValue::Standard(standard) => InstrumentValue::Standard(standard.clone()), + HttpClientValue::Custom(selector) => InstrumentValue::Custom(selector.clone()), + } + } +} + +#[derive(Derivative, Deserialize, JsonSchema, Clone)] +#[serde(deny_unknown_fields, untagged)] +#[derivative(Debug, PartialEq)] +pub(crate) enum HttpClientSelector { + /// A header from the HTTP request + HttpClientRequestHeader { + /// The name of the request header. + request_header: String, + #[serde(skip)] + #[allow(dead_code)] + /// Optional redaction pattern. + redact: Option, + /// Optional default value. + default: Option, + }, + /// A header from the HTTP response + HttpClientResponseHeader { + /// The name of the response header. + response_header: String, + #[serde(skip)] + #[allow(dead_code)] + /// Optional redaction pattern. + redact: Option, + /// Optional default value. + default: Option, + }, +} + +impl Selector for HttpClientSelector { + type Request = http::HttpRequest; + type Response = http::HttpResponse; + type EventResponse = (); + + fn on_request(&self, request: &http::HttpRequest) -> Option { + match self { + HttpClientSelector::HttpClientRequestHeader { + request_header, + default, + .. + } => request + .http_request + .headers() + .get(request_header) + .and_then(|h| h.to_str().ok()) + .map(|h| h.to_string()) + .or_else(|| default.clone()) + .map(opentelemetry::Value::from), + HttpClientSelector::HttpClientResponseHeader { default, .. } => { + default.clone().map(opentelemetry::Value::from) + } + } + } + + fn on_response(&self, response: &http::HttpResponse) -> Option { + match self { + HttpClientSelector::HttpClientRequestHeader { default, .. } => { + default.clone().map(opentelemetry::Value::from) + } + HttpClientSelector::HttpClientResponseHeader { + response_header, + default, + .. + } => response + .http_response + .headers() + .get(response_header) + .and_then(|h| h.to_str().ok()) + .map(|h| h.to_string()) + .or_else(|| default.clone()) + .map(opentelemetry::Value::from), + } + } + + fn on_error(&self, _error: &BoxError, _ctx: &Context) -> Option { + match self { + HttpClientSelector::HttpClientRequestHeader { default, .. } => { + default.clone().map(opentelemetry::Value::from) + } + HttpClientSelector::HttpClientResponseHeader { default, .. } => { + default.clone().map(opentelemetry::Value::from) + } + } + } + + fn is_active(&self, stage: Stage) -> bool { + match self { + HttpClientSelector::HttpClientRequestHeader { .. } => matches!(stage, Stage::Request), + HttpClientSelector::HttpClientResponseHeader { .. } => matches!(stage, Stage::Response), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::Context; + + #[test] + fn test_http_client_request_header() { + let selector = HttpClientSelector::HttpClientRequestHeader { + request_header: "content-type".to_string(), + redact: None, + default: None, + }; + + let http_request = ::http::Request::builder() + .method(::http::Method::GET) + .uri("http://localhost/graphql") + .header("content-type", "application/json") + .body(crate::services::router::body::empty()) + .unwrap(); + + let request = http::HttpRequest { + http_request, + context: Context::new(), + }; + + assert_eq!( + selector.on_request(&request), + Some(opentelemetry::Value::String( + "application/json".to_string().into() + )) + ); + } + + #[test] + fn test_http_client_response_header() { + let selector = HttpClientSelector::HttpClientResponseHeader { + response_header: "content-length".to_string(), + redact: None, + default: None, + }; + + let http_response = ::http::Response::builder() + .status(200) + .header("content-length", "1024") + .body(crate::services::router::body::empty()) + .unwrap(); + + let response = http::HttpResponse { + http_response, + context: Context::new(), + }; + + assert_eq!( + selector.on_response(&response), + Some(opentelemetry::Value::String("1024".to_string().into())) + ); + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/http_client/spans.rs b/apollo-router/src/plugins/telemetry/config_new/http_client/spans.rs new file mode 100644 index 0000000000..b6b37bb046 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/config_new/http_client/spans.rs @@ -0,0 +1,27 @@ +use schemars::JsonSchema; +use serde::Deserialize; + +use crate::plugins::telemetry::config_new::DefaultForLevel; +use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; +use crate::plugins::telemetry::config_new::conditional::Conditional; +use crate::plugins::telemetry::config_new::extendable::Extendable; +use crate::plugins::telemetry::config_new::http_client::attributes::HttpClientAttributes; +use crate::plugins::telemetry::config_new::http_client::selectors::HttpClientSelector; +use crate::plugins::telemetry::otlp::TelemetryDataKind; + +#[derive(Deserialize, JsonSchema, Clone, Debug, Default)] +#[serde(deny_unknown_fields, default)] +pub(crate) struct HttpClientSpans { + /// Custom attributes that are attached to the HTTP client span. + pub(crate) attributes: Extendable>, +} + +impl DefaultForLevel for HttpClientSpans { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.attributes.defaults_for_level(requirement_level, kind); + } +} diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index bb9fc5dbf0..c50cb5e75f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -26,6 +26,7 @@ pub(crate) mod cost; pub(crate) mod events; pub(crate) mod extendable; pub(crate) mod graphql; +pub(crate) mod http_client; pub(crate) mod http_common; pub(crate) mod http_server; pub(crate) mod instruments; diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index 5dcfdfe9ad..f315626f71 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -2,6 +2,7 @@ use schemars::JsonSchema; use serde::Deserialize; use super::connector::spans::ConnectorSpans; +use super::http_client::spans::HttpClientSpans; use super::router::spans::RouterSpans; use super::subgraph::spans::SubgraphSpans; use super::supergraph::spans::SupergraphSpans; @@ -35,6 +36,10 @@ pub(crate) struct Spans { /// Attributes to include on the connector span. /// Connector spans contain information about the connector request and response and therefore contain connector specific attributes. pub(crate) connector: ConnectorSpans, + + /// Attributes to include on the HTTP client span. + /// HTTP client spans contain information about HTTP requests made to subgraphs, including any changes made by Rhai scripts. + pub(crate) http_client: HttpClientSpans, } impl Spans { @@ -52,6 +57,10 @@ impl Spans { self.default_attribute_requirement_level, TelemetryDataKind::Traces, ); + self.http_client.defaults_for_levels( + self.default_attribute_requirement_level, + TelemetryDataKind::Traces, + ); } pub(crate) fn validate(&self) -> Result<(), String> { @@ -70,6 +79,11 @@ impl Spans { .validate() .map_err(|err| format!("error for subgraph span attribute {name:?}: {err}"))?; } + for (name, custom) in &self.http_client.attributes.custom { + custom + .validate() + .map_err(|err| format!("error for http_client span attribute {name:?}: {err}"))?; + } Ok(()) } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 55412a1747..44f3f2bb79 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -296,6 +296,12 @@ impl EnabledFeatures { } } +// Struct to hold request attributes for the http client in context +#[derive(Clone, Debug)] +pub(crate) struct HttpClientAttributes { + pub(crate) attributes: Vec, +} + #[async_trait::async_trait] impl PluginPrivate for Telemetry { type Config = config::Conf; @@ -1095,6 +1101,45 @@ impl PluginPrivate for Telemetry { .boxed() } + fn http_client_service( + &self, + _subgraph_name: &str, + service: crate::services::http::BoxService, + ) -> crate::services::http::BoxService { + let req_fn_config = self.config.clone(); + let res_fn_config = self.config.clone(); + + ServiceBuilder::new() + .map_request(move |request: crate::services::http::HttpRequest| { + // Get and store attributes so that they can be applied later after the span is created + let client_attributes = HttpClientAttributes { + attributes: req_fn_config + .instrumentation + .spans + .http_client + .attributes + .on_request(&request), + }; + request.context.extensions().with_lock(|lock| { + lock.insert(client_attributes); + }); + + request + }) + .map_response(move |response: crate::services::http::HttpResponse| { + let attributes = res_fn_config + .instrumentation + .spans + .http_client + .attributes + .on_response(&response); + ::tracing::Span::current().set_span_dyn_attributes(attributes); + response + }) + .service(service) + .boxed() + } + fn web_endpoints(&self) -> MultiMap { self.custom_endpoints.clone() } diff --git a/apollo-router/src/services/http/service.rs b/apollo-router/src/services/http/service.rs index 7d1866dede..5afcee235e 100644 --- a/apollo-router/src/services/http/service.rs +++ b/apollo-router/src/services/http/service.rs @@ -36,7 +36,9 @@ use crate::axum_factory::compression::Compressor; use crate::configuration::TlsClientAuth; use crate::error::FetchError; use crate::plugins::authentication::subgraph::SigningParamsConfig; +use crate::plugins::telemetry::HttpClientAttributes; use crate::plugins::telemetry::consts::HTTP_REQUEST_SPAN_NAME; +use crate::plugins::telemetry::dynamic_attribute::SpanDynAttribute; use crate::plugins::telemetry::otel::OpenTelemetrySpanExt; use crate::plugins::telemetry::reload::otel::prepare_context; use crate::plugins::traffic_shaping::Http2Config; @@ -313,9 +315,15 @@ impl tower::Service for HttpClientService { "http.route" = %path, "http.url" = %schema_uri, "net.transport" = "ip_tcp", - //"apollo.subgraph.name" = %service_name, - //"graphql.operation.name" = %operation_name, ); + + // Apply any attributes that were stored by telemetry middleware + if let Some(client_attributes) = context + .extensions() + .with_lock(|lock| lock.get::().cloned()) + { + http_req_span.set_span_dyn_attributes(client_attributes.attributes); + } get_text_map_propagator(|propagator| { propagator.inject_context( &prepare_context(http_req_span.context()), diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header-2.snap index 7f5bcc7b3e..d069ee55b1 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header.snap b/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header.snap index 7f5bcc7b3e..d069ee55b1 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__batch_send_header.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id-2.snap index 16a24340b1..fec5016370 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id.snap b/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id.snap index 16a24340b1..fec5016370 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__batch_trace_id.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__client_name-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__client_name-2.snap index 002cf3d71c..0791fe568d 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__client_name-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__client_name-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__client_name.snap b/apollo-router/tests/snapshots/apollo_otel_traces__client_name.snap index 002cf3d71c..0791fe568d 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__client_name.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__client_name.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__client_version-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__client_version-2.snap index 1354627235..eaf3ec9fd2 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__client_version-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__client_version-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__client_version.snap b/apollo-router/tests/snapshots/apollo_otel_traces__client_version.snap index 1354627235..eaf3ec9fd2 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__client_version.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__client_version.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__condition_else-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__condition_else-2.snap index 0921dc9256..0c45204836 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__condition_else-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__condition_else-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__condition_else.snap b/apollo-router/tests/snapshots/apollo_otel_traces__condition_else.snap index 0921dc9256..0c45204836 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__condition_else.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__condition_else.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__condition_if-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__condition_if-2.snap index 7ab763e2fc..2275283e09 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__condition_if-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__condition_if-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__condition_if.snap b/apollo-router/tests/snapshots/apollo_otel_traces__condition_if.snap index 7ab763e2fc..2275283e09 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__condition_if.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__condition_if.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__non_defer-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__non_defer-2.snap index e11f185c53..96a3eed566 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__non_defer-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__non_defer-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__non_defer.snap b/apollo-router/tests/snapshots/apollo_otel_traces__non_defer.snap index e11f185c53..96a3eed566 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__non_defer.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__non_defer.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__send_header-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__send_header-2.snap index 293fe3eb7c..d090a76689 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__send_header-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__send_header-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__send_header.snap b/apollo-router/tests/snapshots/apollo_otel_traces__send_header.snap index 293fe3eb7c..d090a76689 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__send_header.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__send_header.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value-2.snap index 85ad5c7909..a6daaabc5b 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value.snap b/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value.snap index 85ad5c7909..a6daaabc5b 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__send_variable_value.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__trace_id-2.snap b/apollo-router/tests/snapshots/apollo_otel_traces__trace_id-2.snap index e11f185c53..96a3eed566 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__trace_id-2.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__trace_id-2.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/apollo-router/tests/snapshots/apollo_otel_traces__trace_id.snap b/apollo-router/tests/snapshots/apollo_otel_traces__trace_id.snap index e11f185c53..96a3eed566 100644 --- a/apollo-router/tests/snapshots/apollo_otel_traces__trace_id.snap +++ b/apollo-router/tests/snapshots/apollo_otel_traces__trace_id.snap @@ -1,7 +1,6 @@ --- source: apollo-router/tests/apollo_otel_traces.rs expression: report -snapshot_kind: text --- resourceSpans: - resource: diff --git a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/selectors.mdx b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/selectors.mdx index 42c40daafd..1b51256976 100644 --- a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/selectors.mdx +++ b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/selectors.mdx @@ -105,6 +105,15 @@ The subgraph service executes multiple times during query execution, with each e | `error` | No | `reason` | A string value containing error reason when it's a critical error | | `cache` | No | `hit` \| `miss` | Returns the number of cache hit or miss for this subgraph request | +### HTTP Client + +The HTTP client service also executes multiple times, with each execution representing a HTTP request to a single subgraph or REST service. Importantly, this service executes after any Rhai scripts that modify the subgraph requests, so these selectors can be used to observe any headers added by the Rhai script. + +| Selector | Defaultable | Values | Description | +|--------------------|-------------|--------|-------------------------------| +| `request_header` | Yes | | The name of a request header | +| `response_header` | Yes | | The name of a response header | + ### Connector #### HTTP diff --git a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/spans.mdx b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/spans.mdx index 29a086917c..de6e1d7835 100644 --- a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/spans.mdx +++ b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/spans.mdx @@ -18,7 +18,7 @@ A **span** captures contextual information about requests and responses as they' -The `router`, `supergraph`, `subgraph` and `connector` sections are used to define custom span configuration for each service: +The `router`, `supergraph`, `subgraph`, `connector`, and `http_client` sections are used to define custom span configuration for each service: ```yaml title="router.yaml" telemetry: @@ -36,6 +36,9 @@ telemetry: connector: # highlight-line attributes: {} # ... + http_client: # highlight-line + attributes: {} + # ... ``` ### `attributes` @@ -246,6 +249,9 @@ telemetry: connector: attributes: {} # ... + http_client: + attributes: {} + # ... ``` ## Spans configuration reference