From a04974797cf56be95b533e0430add5e5bc4bad59 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:02:16 +0100 Subject: [PATCH 01/25] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/metrics/aggregation.rs | 1 + apollo-router/src/plugins/telemetry/config.rs | 3 +- .../telemetry/config_new/instruments.rs | 323 +++++++++++++++++- apollo-router/src/plugins/telemetry/mod.rs | 36 +- 4 files changed, 339 insertions(+), 24 deletions(-) diff --git a/apollo-router/src/metrics/aggregation.rs b/apollo-router/src/metrics/aggregation.rs index 5f23e7f182..020ce1532d 100644 --- a/apollo-router/src/metrics/aggregation.rs +++ b/apollo-router/src/metrics/aggregation.rs @@ -56,6 +56,7 @@ impl Default for AggregateMeterProvider { inner: Arc::new(Mutex::new(Inner::default())), }; + dbg!("default aggregate meter provider"); // If the regular global meter provider has been set then the aggregate meter provider will use it. Otherwise it'll default to a no-op. // For this to work the global meter provider must be set before the aggregate meter provider is created. // This functionality is not guaranteed to stay like this, so use at your own risk. diff --git a/apollo-router/src/plugins/telemetry/config.rs b/apollo-router/src/plugins/telemetry/config.rs index 7de9717745..61a127c789 100644 --- a/apollo-router/src/plugins/telemetry/config.rs +++ b/apollo-router/src/plugins/telemetry/config.rs @@ -88,9 +88,8 @@ pub(crate) struct Instrumentation { pub(crate) events: config_new::events::Events, /// Span configuration pub(crate) spans: config_new::spans::Spans, - #[serde(skip)] /// Instrument configuration - pub(crate) instruments: config_new::instruments::Instruments, + pub(crate) instruments: config_new::instruments::InstrumentsConfig, } /// Metrics configuration diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 96978b1c10..46b42bb07b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -1,8 +1,19 @@ +use std::collections::LinkedList; use std::fmt::Debug; +use http::header::CONTENT_LENGTH; +use opentelemetry_api::metrics::Counter; +use opentelemetry_api::metrics::Histogram; +use opentelemetry_api::metrics::MeterProvider; +use opentelemetry_api::metrics::Unit; +use opentelemetry_api::metrics::UpDownCounter; +use opentelemetry_api::KeyValue; use schemars::JsonSchema; use serde::Deserialize; +use tower::BoxError; +use super::Selector; +use crate::metrics; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; use crate::plugins::telemetry::config_new::attributes::RouterAttributes; use crate::plugins::telemetry::config_new::attributes::SubgraphAttributes; @@ -12,27 +23,31 @@ use crate::plugins::telemetry::config_new::extendable::Extendable; use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; +use crate::plugins::telemetry::config_new::Selectors; +use crate::services::router; #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -pub(crate) struct Instruments { +pub(crate) struct InstrumentsConfig { /// The attributes to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation. - default_attribute_requirement_level: DefaultAttributeRequirementLevel, + pub(crate) default_attribute_requirement_level: DefaultAttributeRequirementLevel, /// Router service instruments. For more information see documentation on Router lifecycle. - router: Extendable>, + pub(crate) router: + Extendable>, /// Supergraph service instruments. For more information see documentation on Router lifecycle. - supergraph: + pub(crate) supergraph: Extendable>, /// Subgraph service instruments. For more information see documentation on Router lifecycle. - subgraph: Extendable>, + pub(crate) subgraph: + Extendable>, } #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct RouterInstruments { +pub(crate) struct RouterInstrumentsConfig { /// Histogram of server request duration #[serde(rename = "http.server.request.duration")] http_server_request_duration: @@ -68,6 +83,44 @@ impl Default for DefaultedStandardInstrument { } } +impl DefaultedStandardInstrument { + fn is_enabled(&self) -> bool { + match self { + Self::Bool(enabled) => *enabled, + Self::Extendable { .. } => true, + } + } +} + +impl Selectors for DefaultedStandardInstrument +where + T: Selectors, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) -> LinkedList { + match self { + Self::Bool(_) => LinkedList::new(), + Self::Extendable { attributes } => attributes.on_request(request), + } + } + + fn on_response(&self, response: &Self::Response) -> LinkedList { + match self { + Self::Bool(_) => LinkedList::new(), + Self::Extendable { attributes } => attributes.on_response(response), + } + } + + fn on_error(&self, error: &BoxError) -> LinkedList { + match self { + Self::Bool(_) => LinkedList::new(), + Self::Extendable { attributes } => attributes.on_error(error), + } + } +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] @@ -119,6 +172,28 @@ where condition: Condition, } +impl Selectors for Instrument +where + A: Debug + Default + Selectors, + E: Debug + Selector, +{ + type Request = Request; + + type Response = Response; + + fn on_request(&self, request: &Self::Request) -> LinkedList { + todo!() + } + + fn on_response(&self, response: &Self::Response) -> LinkedList { + todo!() + } + + fn on_error(&self, error: &BoxError) -> LinkedList { + todo!() + } +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug)] #[serde(deny_unknown_fields, rename_all = "snake_case")] @@ -126,14 +201,12 @@ pub(crate) enum InstrumentType { /// A monotonic counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums Counter, - /// A counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums - UpDownCounter, - + // /// A counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums + // UpDownCounter, /// A histogram https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram Histogram, - - /// A gauge https://opentelemetry.io/docs/specs/otel/metrics/data-model/#gauge - Gauge, + // /// A gauge https://opentelemetry.io/docs/specs/otel/metrics/data-model/#gauge + // Gauge, } #[allow(dead_code)] @@ -150,5 +223,229 @@ pub(crate) enum InstrumentValue { pub(crate) enum Standard { Duration, Unit, - Active, + // Active, +} + +struct ActiveRequestGuard(UpDownCounter, Vec); + +impl ActiveRequestGuard { + fn new(counter: UpDownCounter, attrs: Vec) -> Self { + counter.add(1, &attrs); + Self(counter, attrs) + } +} + +impl Drop for ActiveRequestGuard { + fn drop(&mut self) { + self.0.add(-1, &self.1); + } +} + +pub(crate) trait Instrumented { + type Request; + type Response; + + fn on_request(&self, request: &Self::Request); + fn on_response(&self, response: &Self::Response); + fn on_error(&self, error: &BoxError); +} + +impl Instrumented for RouterInstrumentsConfig { + type Request = router::Request; + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) { + let meter = metrics::meter_provider().meter("apollo/router"); + if self.http_server_active_requests.is_enabled() { + let attrs = self + .http_server_active_requests + .on_request(request) + .into_iter() + .collect::>(); + let active_req_guard = ActiveRequestGuard::new( + meter + .i64_up_down_counter("http.server.active_requests") + .init(), + attrs, + ); + request.context.extensions().lock().insert(active_req_guard); + } + + if self.http_server_request_body_size.is_enabled() { + let body_size = request + .router_request + .headers() + .get(&CONTENT_LENGTH) + .and_then(|val| val.to_str().ok()?.parse::().ok()); + if let Some(body_size) = body_size { + match meter + .u64_histogram("http.server.request.body.size") + .try_init() + { + Ok(histogram) => { + let attrs = self + .http_server_request_body_size + .on_request(request) + .into_iter() + .collect::>(); + histogram.record(body_size, &attrs); + } + Err(err) => { + tracing::error!( + "cannot create gauge for 'http.server.request.body.size': {err:?}" + ); + } + } + } + } + } + + fn on_response(&self, response: &Self::Response) { + let meter = metrics::meter_provider().meter("apollo/router"); + if self.http_server_request_duration.is_enabled() { + let attrs = self + .http_server_request_duration + .on_response(response) + .into_iter() + .collect::>(); + let request_duration = response.context.busy_time(); + match meter + .f64_histogram("http.server.request.duration") + .with_unit(Unit::new("s")) + .try_init() + { + Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), + Err(_) => todo!(), + } + } + + if self.http_server_response_body_size.is_enabled() { + let body_size = response + .response + .headers() + .get(&CONTENT_LENGTH) + .and_then(|val| val.to_str().ok()?.parse::().ok()); + if let Some(body_size) = body_size { + match meter + .u64_histogram("http.server.response.body.size") + .try_init() + { + Ok(histogram) => { + let attrs = self + .http_server_response_body_size + .on_response(response) + .into_iter() + .collect::>(); + histogram.record(body_size, &attrs); + } + Err(err) => { + tracing::error!( + "cannot create gauge for 'http.server.response.body.size': {err:?}" + ); + } + } + } + } + } + + fn on_error(&self, error: &BoxError) { + let meter = metrics::meter_provider().meter("apollo/router"); + // FIXME: Can't use the context here + // if self.http_server_request_duration.is_enabled() { + // let attrs = self + // .http_server_request_duration + // .on_response(response) + // .into_iter() + // .collect::>(); + // let request_duration = response.context.busy_time(); + // match meter + // .f64_histogram("http.server.request.duration") + // .with_unit(Unit::new("s")) + // .try_init() + // { + // Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), + // Err(_) => todo!(), + // } + // } + } +} + +impl Instrumented for Extendable> +where + A: Default + Instrumented, + B: Default + Debug + Selectors, + E: Debug + Selector, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) { + self.attributes.on_request(request); + // TODO custom + // for (key, instr) in &self.custom { + // let attrs = instr.on_request(request); + + // } + } + + fn on_response(&self, response: &Self::Response) { + self.attributes.on_response(response); + // TODO custom + } + + fn on_error(&self, error: &BoxError) { + self.attributes.on_error(error); + // TODO custom + } +} + +impl Selectors for RouterInstrumentsConfig { + type Request = router::Request; + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) -> LinkedList { + let mut attrs = self.http_server_active_requests.on_request(request); + attrs.extend(self.http_server_request_body_size.on_request(request)); + attrs.extend(self.http_server_request_duration.on_request(request)); + attrs.extend(self.http_server_response_body_size.on_request(request)); + + attrs + } + + fn on_response(&self, response: &Self::Response) -> LinkedList { + let mut attrs = self.http_server_active_requests.on_response(response); + attrs.extend(self.http_server_request_body_size.on_response(response)); + attrs.extend(self.http_server_request_duration.on_response(response)); + attrs.extend(self.http_server_response_body_size.on_response(response)); + + attrs + } + + fn on_error(&self, error: &BoxError) -> LinkedList { + let mut attrs = self.http_server_active_requests.on_error(error); + attrs.extend(self.http_server_request_body_size.on_error(error)); + attrs.extend(self.http_server_request_duration.on_error(error)); + attrs.extend(self.http_server_response_body_size.on_error(error)); + + attrs + } +} + +#[derive(Debug, Clone)] +struct RouterInstruments { + /// Histogram of server request duration + http_server_request_duration: Histogram, + /// Gauge of active requests + http_server_active_requests: UpDownCounter, + /// Histogram of server request body size + http_server_request_body_size: Histogram, + /// Histogram of server response body size + http_server_response_body_size: Histogram, + /// Config + config: RouterInstrumentsConfig, +} + +struct CustomInstruments { + counters: Vec>, + histograms: Vec>, } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index f35c75b8b5..d2080d4819 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -60,6 +60,7 @@ use self::config::Conf; use self::config::Sampler; use self::config::SamplerOption; use self::config::TraceIdFormat; +use self::config_new::instruments::Instrumented; use self::config_new::spans::Spans; use self::metrics::apollo::studio::SingleTypeStat; use self::metrics::AttributesForwardConf; @@ -241,21 +242,24 @@ impl Plugin for Telemetry { if config.instrumentation.spans.mode == SpanMode::Deprecated { ::tracing::warn!("telemetry.instrumentation.spans.mode is currently set to 'deprecated', either explicitly or via defaulting. Set telemetry.instrumentation.spans.mode explicitly in your router.yaml to 'spec_compliant' for log and span attributes that follow OpenTelemetry semantic conventions. This option will be defaulted to 'spec_compliant' in a future release and eventually removed altogether"); } + let public_meter_provider = Some(FilterMeterProvider::public( + metrics_builder.public_meter_provider_builder.build(), + )); + let private_meter_provider = Some(FilterMeterProvider::private( + metrics_builder.apollo_meter_provider_builder.build(), + )); + let public_prometheus_meter_provider = metrics_builder + .prometheus_meter_provider + .map(FilterMeterProvider::public); Ok(Telemetry { custom_endpoints: metrics_builder.custom_endpoints, apollo_metrics_sender: metrics_builder.apollo_metrics_sender, field_level_instrumentation_ratio, tracer_provider: Some(tracer_provider), - public_meter_provider: Some(FilterMeterProvider::public( - metrics_builder.public_meter_provider_builder.build(), - )), - private_meter_provider: Some(FilterMeterProvider::private( - metrics_builder.apollo_meter_provider_builder.build(), - )), - public_prometheus_meter_provider: metrics_builder - .prometheus_meter_provider - .map(FilterMeterProvider::public), + public_meter_provider, + private_meter_provider, + public_prometheus_meter_provider, sampling_filter_ratio, config: Arc::new(config), is_active: false, @@ -335,6 +339,12 @@ impl Plugin for Telemetry { .router .attributes .on_request(request); + + config_request + .instrumentation + .instruments + .router + .on_request(request); custom_attributes.extend([ KeyValue::new(CLIENT_NAME_KEY, client_name.to_string()), KeyValue::new(CLIENT_VERSION_KEY, client_version.to_string()), @@ -375,6 +385,11 @@ impl Plugin for Telemetry { .attributes .on_response(response), ); + config + .instrumentation + .instruments + .router + .on_response(response); if expose_trace_id.enabled { if let Some(header_name) = &expose_trace_id.header_name { let mut headers: HashMap> = @@ -406,6 +421,7 @@ impl Plugin for Telemetry { span.set_dyn_attributes( config.instrumentation.spans.router.attributes.on_error(err), ); + config.instrumentation.instruments.router.on_error(err); } response @@ -1482,6 +1498,8 @@ impl Telemetry { old_meter_providers[2] = meter_provider.set(MeterProviderType::Public, self.public_meter_provider.take()); + dbg!("reload metrics"); + metrics_layer().clear(); Self::checked_meter_shutdown(old_meter_providers); From 6913b169f80ba93799f164f9dab5d7373e711eed Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:52:54 +0100 Subject: [PATCH 02/25] standard subgraph instruments Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 228 +++++++++++++++--- apollo-router/src/plugins/telemetry/mod.rs | 20 +- 2 files changed, 216 insertions(+), 32 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 46b42bb07b..ffcbfa8daf 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -10,6 +10,7 @@ use opentelemetry_api::metrics::UpDownCounter; use opentelemetry_api::KeyValue; use schemars::JsonSchema; use serde::Deserialize; +use tokio::time::Instant; use tower::BoxError; use super::Selector; @@ -25,6 +26,8 @@ use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::plugins::telemetry::config_new::Selectors; use crate::services::router; +use crate::services::subgraph; +use crate::Context; #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] @@ -37,11 +40,11 @@ pub(crate) struct InstrumentsConfig { pub(crate) router: Extendable>, /// Supergraph service instruments. For more information see documentation on Router lifecycle. - pub(crate) supergraph: + supergraph: Extendable>, /// Subgraph service instruments. For more information see documentation on Router lifecycle. pub(crate) subgraph: - Extendable>, + Extendable>, } #[allow(dead_code)] @@ -129,18 +132,21 @@ struct SupergraphInstruments {} #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct SubgraphInstruments { +pub(crate) struct SubgraphInstrumentsConfig { /// Histogram of client request duration #[serde(rename = "http.client.request.duration")] - http_client_request_duration: bool, + http_client_request_duration: + DefaultedStandardInstrument>, /// Histogram of client request body size #[serde(rename = "http.client.request.body.size")] - http_client_request_body_size: bool, + http_client_request_body_size: + DefaultedStandardInstrument>, /// Histogram of client response body size #[serde(rename = "http.client.response.body.size")] - http_client_response_body_size: bool, + http_client_response_body_size: + DefaultedStandardInstrument>, } #[allow(dead_code)] @@ -247,7 +253,7 @@ pub(crate) trait Instrumented { fn on_request(&self, request: &Self::Request); fn on_response(&self, response: &Self::Response); - fn on_error(&self, error: &BoxError); + fn on_error(&self, error: &BoxError, ctx: &Context); } impl Instrumented for RouterInstrumentsConfig { @@ -292,7 +298,7 @@ impl Instrumented for RouterInstrumentsConfig { } Err(err) => { tracing::error!( - "cannot create gauge for 'http.server.request.body.size': {err:?}" + "cannot create histogram for 'http.server.request.body.size': {err:?}" ); } } @@ -315,7 +321,11 @@ impl Instrumented for RouterInstrumentsConfig { .try_init() { Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - Err(_) => todo!(), + Err(err) => { + tracing::error!( + "cannot create gauge for 'http.server.request.duration': {err:?}" + ); + } } } @@ -348,25 +358,156 @@ impl Instrumented for RouterInstrumentsConfig { } } - fn on_error(&self, error: &BoxError) { + fn on_error(&self, error: &BoxError, ctx: &Context) { let meter = metrics::meter_provider().meter("apollo/router"); - // FIXME: Can't use the context here - // if self.http_server_request_duration.is_enabled() { - // let attrs = self - // .http_server_request_duration - // .on_response(response) - // .into_iter() - // .collect::>(); - // let request_duration = response.context.busy_time(); - // match meter - // .f64_histogram("http.server.request.duration") - // .with_unit(Unit::new("s")) - // .try_init() - // { - // Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - // Err(_) => todo!(), - // } - // } + if self.http_server_request_duration.is_enabled() { + let attrs = self + .http_server_request_duration + .on_error(error) + .into_iter() + .collect::>(); + let request_duration = ctx.busy_time(); + match meter + .f64_histogram("http.server.request.duration") + .with_unit(Unit::new("s")) + .try_init() + { + Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), + Err(err) => { + tracing::error!( + "cannot create gauge for 'http.server.request.duration': {err:?}" + ); + } + } + } + } +} + +struct SubgraphInstant(Instant); + +impl Instrumented for SubgraphInstrumentsConfig { + type Request = subgraph::Request; + type Response = subgraph::Response; + + fn on_request(&self, request: &Self::Request) { + let meter = metrics::meter_provider().meter("apollo/router"); + request + .context + .extensions() + .lock() + .insert(SubgraphInstant(Instant::now())); + if self.http_client_request_body_size.is_enabled() { + let body_size = request + .subgraph_request + .headers() + .get(&CONTENT_LENGTH) + .and_then(|val| val.to_str().ok()?.parse::().ok()); + if let Some(body_size) = body_size { + match meter + .u64_histogram("http.client.request.body.size") + .try_init() + { + Ok(histogram) => { + let attrs = self + .http_client_request_body_size + .on_request(request) + .into_iter() + .collect::>(); + histogram.record(body_size, &attrs); + } + Err(err) => { + tracing::error!( + "cannot create gauge for 'http.client.request.body.size': {err:?}" + ); + } + } + } + } + } + + fn on_response(&self, response: &Self::Response) { + let meter = metrics::meter_provider().meter("apollo/router"); + if self.http_client_request_duration.is_enabled() { + let attrs = self + .http_client_request_duration + .on_response(response) + .into_iter() + .collect::>(); + let request_duration = response + .context + .extensions() + .lock() + .get::() + .map(|i| i.0.elapsed()); + if let Some(request_duration) = request_duration { + match meter + .f64_histogram("http.client.request.duration") + .with_unit(Unit::new("s")) + .try_init() + { + Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), + Err(err) => { + tracing::error!( + "cannot create histogram for 'http.client.request.duration': {err:?}" + ); + } + } + } + } + + if self.http_client_response_body_size.is_enabled() { + let body_size = response + .response + .headers() + .get(&CONTENT_LENGTH) + .and_then(|val| val.to_str().ok()?.parse::().ok()); + if let Some(body_size) = body_size { + match meter + .u64_histogram("http.client.response.body.size") + .try_init() + { + Ok(histogram) => { + let attrs = self + .http_client_response_body_size + .on_response(response) + .into_iter() + .collect::>(); + histogram.record(body_size, &attrs); + } + Err(err) => { + tracing::error!( + "cannot create histogram for 'http.client.response.body.size': {err:?}" + ); + } + } + } + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + let meter = metrics::meter_provider().meter("apollo/router"); + if self.http_client_request_duration.is_enabled() { + let attrs = self + .http_client_request_duration + .on_error(error) + .into_iter() + .collect::>(); + let request_duration = ctx + .extensions() + .lock() + .get::() + .map(|i| i.0.elapsed()); + if let Some(request_duration) = request_duration { + match meter + .f64_histogram("http.client.request.duration") + .with_unit(Unit::new("s")) + .try_init() + { + Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), + Err(_) => todo!(), + } + } + } } } @@ -393,8 +534,8 @@ where // TODO custom } - fn on_error(&self, error: &BoxError) { - self.attributes.on_error(error); + fn on_error(&self, error: &BoxError, ctx: &Context) { + self.attributes.on_error(error, ctx); // TODO custom } } @@ -431,6 +572,35 @@ impl Selectors for RouterInstrumentsConfig { } } +impl Selectors for SubgraphInstrumentsConfig { + type Request = subgraph::Request; + type Response = subgraph::Response; + + fn on_request(&self, request: &Self::Request) -> LinkedList { + let mut attrs = self.http_client_request_body_size.on_request(request); + attrs.extend(self.http_client_request_duration.on_request(request)); + attrs.extend(self.http_client_response_body_size.on_request(request)); + + attrs + } + + fn on_response(&self, response: &Self::Response) -> LinkedList { + let mut attrs = self.http_client_request_body_size.on_response(response); + attrs.extend(self.http_client_request_duration.on_response(response)); + attrs.extend(self.http_client_response_body_size.on_response(response)); + + attrs + } + + fn on_error(&self, error: &BoxError) -> LinkedList { + let mut attrs = self.http_client_request_body_size.on_error(error); + attrs.extend(self.http_client_request_duration.on_error(error)); + attrs.extend(self.http_client_response_body_size.on_error(error)); + + attrs + } +} + #[derive(Debug, Clone)] struct RouterInstruments { /// Histogram of server request duration diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index d2080d4819..79b045a436 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -357,9 +357,9 @@ impl Plugin for Telemetry { ), ]); - custom_attributes + (custom_attributes, request.context.clone()) }, - move |custom_attributes: LinkedList, fut| { + move |(custom_attributes, ctx): (LinkedList, Context), fut| { let start = Instant::now(); let config = config_later.clone(); @@ -421,7 +421,11 @@ impl Plugin for Telemetry { span.set_dyn_attributes( config.instrumentation.spans.router.attributes.on_error(err), ); - config.instrumentation.instruments.router.on_error(err); + config + .instrumentation + .instruments + .router + .on_error(err, &ctx); } response @@ -586,6 +590,11 @@ impl Plugin for Telemetry { .subgraph .attributes .on_request(sub_request); + config + .instrumentation + .instruments + .subgraph + .on_request(sub_request); (sub_request.context.clone(), custom_attributes) }, @@ -615,6 +624,7 @@ impl Plugin for Telemetry { .attributes .on_response(resp), ); + conf.instrumentation.instruments.subgraph.on_response(resp); } Err(err) => { span.record(OTEL_STATUS_CODE, "Error"); @@ -622,6 +632,10 @@ impl Plugin for Telemetry { span.set_dyn_attributes( conf.instrumentation.spans.subgraph.attributes.on_error(err), ); + conf.instrumentation + .instruments + .subgraph + .on_error(err, &context); } } From 224352bf9dbcf22a76882f60df8823d4374f05dd Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:04:52 +0100 Subject: [PATCH 03/25] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 205 ++++++++++++++++-- 1 file changed, 190 insertions(+), 15 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index ffcbfa8daf..8987f28b23 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -1,5 +1,7 @@ +use std::collections::HashMap; use std::collections::LinkedList; use std::fmt::Debug; +use std::sync::Arc; use http::header::CONTENT_LENGTH; use opentelemetry_api::metrics::Counter; @@ -8,6 +10,7 @@ use opentelemetry_api::metrics::MeterProvider; use opentelemetry_api::metrics::Unit; use opentelemetry_api::metrics::UpDownCounter; use opentelemetry_api::KeyValue; +use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; use tokio::time::Instant; @@ -256,6 +259,15 @@ pub(crate) trait Instrumented { fn on_error(&self, error: &BoxError, ctx: &Context); } +pub(crate) trait InstrumentedMut { + type Request; + type Response; + + fn on_request(&mut self, request: &Self::Request); + fn on_response(&mut self, response: &Self::Response); + fn on_error(&mut self, error: &BoxError, ctx: &Context); +} + impl Instrumented for RouterInstrumentsConfig { type Request = router::Request; type Response = router::Response; @@ -601,21 +613,184 @@ impl Selectors for SubgraphInstrumentsConfig { } } -#[derive(Debug, Clone)] -struct RouterInstruments { - /// Histogram of server request duration - http_server_request_duration: Histogram, - /// Gauge of active requests - http_server_active_requests: UpDownCounter, - /// Histogram of server request body size - http_server_request_body_size: Histogram, - /// Histogram of server response body size - http_server_response_body_size: Histogram, - /// Config - config: RouterInstrumentsConfig, +pub(crate) struct RouterCustomInstruments { + counters: + Vec>, +} + +impl RouterCustomInstruments { + pub(crate) fn new( + config: &HashMap>, + ) -> Self { + let mut counters = Vec::new(); + for (instrument_name, instrument) in config { + // FIXME: I think we should not set the value + // let value = match instrument.value { + // InstrumentValue::Standard(Standard::Unit) => Increment::Unit, + // InstrumentValue::Standard(Standard::Duration) => { + // Increment::Duration(Instant::now()) + // } + // InstrumentValue::Custom(selector) => { + // todo!() + // } + // }; + + match instrument.ty { + InstrumentType::Counter => { + // let counter = CustomCounter { + // increment: todo!(), + // counter: None, + // attributes: Vec::new(), + // }; + } + InstrumentType::Histogram => todo!(), + } + } + + Self { counters } + } +} + +impl Instrumented for RouterCustomInstruments { + type Request = router::Request; + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) { + for counter in &self.counters { + match counter.increment { + Increment::Unit => todo!(), + Increment::Duration(_) => todo!(), + Increment::Custom(_) => todo!(), + } + } + } + + fn on_response(&self, response: &Self::Response) { + todo!() + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + todo!() + } +} + +// Une struct qui wrap counter/histogram +// Qui prend l'increment (unit/duration) +// Qui prend les attributs +// Qui implemente drop, si ça drop et que l'instrument est None donc on l'a take ça veut dire qu'il a été utilisé et donc on incremente pas au drop +// Sinon on incremente et on met un label error_code="broken_pipe" + +struct CustomCounter +where + A: Selectors + Default, + T: Selector, +{ + inner: Mutex>, +} + +struct CustomCounterInner +where + A: Selectors + Default, + T: Selector, +{ + increment: Increment, + selector: Option>, + selectors: Extendable, + counter: Option>, + attributes: Vec, +} + +enum Increment { + Unit, + Duration(Instant), + Custom(Option), } -struct CustomInstruments { - counters: Vec>, - histograms: Vec>, +impl Instrumented for CustomCounter +where + A: Selectors + Default, + T: Selector, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) { + let mut inner = self.inner.lock(); + inner.attributes = inner.selectors.on_request(request).into_iter().collect(); + if let Some(selected_value) = inner.selector.as_ref().and_then(|s| s.on_request(request)) { + inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) + } + } + + fn on_response(&self, response: &Self::Response) { + let mut inner = self.inner.lock(); + let mut attrs: Vec = inner.selectors.on_response(response).into_iter().collect(); + attrs.extend(inner.attributes); + + if let Some(selected_value) = inner + .selector + .as_ref() + .and_then(|s| s.on_response(response)) + { + inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) + } + // Call actual metric macros + + let increment = match inner.increment { + Increment::Unit => 1f64, + Increment::Duration(instant) => instant.elapsed().as_secs_f64(), + Increment::Custom(val) => match val { + Some(incr) => incr as f64, + None => 0f64, + }, + }; + + if let Some(counter) = inner.counter.take() { + counter.add(increment, &attrs); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + let mut inner = self.inner.lock(); + let mut attrs: Vec = inner.selectors.on_error(error).into_iter().collect(); + attrs.extend(inner.attributes); + + // Call actual metric macros + + let increment = match inner.increment { + Increment::Unit => 1f64, + Increment::Duration(instant) => instant.elapsed().as_secs_f64(), + Increment::Custom(val) => match val { + Some(incr) => incr as f64, + None => 0f64, + }, + }; + + if let Some(counter) = inner.counter.take() { + counter.add(increment, &attrs); + } + } +} + +impl Drop for CustomCounter +where + A: Selectors + Default, + T: Selector, +{ + fn drop(&mut self) { + let inner = self.inner.try_lock(); + if let Some(inner) = inner { + if let Some(counter) = inner.counter.take() { + let incr: f64 = match &inner.increment { + Increment::Unit => 1f64, + Increment::Duration(instant) => instant.elapsed().as_secs_f64(), + Increment::Custom(val) => match val { + Some(incr) => incr as f64, + None => 0f64, + }, + }; + counter.add(incr, &inner.attributes); + } + } + } } From 40e60b0913cceb7bbb9c66f13c16acdd1aeac7d4 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:41:01 +0100 Subject: [PATCH 04/25] add support of histogram Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 232 ++++++++++++++---- apollo-router/src/plugins/telemetry/mod.rs | 22 +- 2 files changed, 206 insertions(+), 48 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 8987f28b23..ad3f7fdfa1 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -191,15 +191,15 @@ where type Response = Response; fn on_request(&self, request: &Self::Request) -> LinkedList { - todo!() + self.attributes.on_request(request) } fn on_response(&self, response: &Self::Response) -> LinkedList { - todo!() + self.attributes.on_response(response) } fn on_error(&self, error: &BoxError) -> LinkedList { - todo!() + self.attributes.on_error(error) } } @@ -534,21 +534,14 @@ where fn on_request(&self, request: &Self::Request) { self.attributes.on_request(request); - // TODO custom - // for (key, instr) in &self.custom { - // let attrs = instr.on_request(request); - - // } } fn on_response(&self, response: &Self::Response) { self.attributes.on_response(response); - // TODO custom } fn on_error(&self, error: &BoxError, ctx: &Context) { self.attributes.on_error(error, ctx); - // TODO custom } } @@ -616,6 +609,8 @@ impl Selectors for SubgraphInstrumentsConfig { pub(crate) struct RouterCustomInstruments { counters: Vec>, + histograms: + Vec>, } impl RouterCustomInstruments { @@ -623,31 +618,68 @@ impl RouterCustomInstruments { config: &HashMap>, ) -> Self { let mut counters = Vec::new(); - for (instrument_name, instrument) in config { - // FIXME: I think we should not set the value - // let value = match instrument.value { - // InstrumentValue::Standard(Standard::Unit) => Increment::Unit, - // InstrumentValue::Standard(Standard::Duration) => { - // Increment::Duration(Instant::now()) - // } - // InstrumentValue::Custom(selector) => { - // todo!() - // } - // }; + let mut histograms = Vec::new(); + let meter = metrics::meter_provider().meter("apollo/router"); + for (instrument_name, instrument) in config { match instrument.ty { InstrumentType::Counter => { - // let counter = CustomCounter { - // increment: todo!(), - // counter: None, - // attributes: Vec::new(), - // }; + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let counter = CustomCounterInner { + increment, + counter: Some(meter.f64_counter(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + counters.push(CustomCounter { + inner: Mutex::new(counter), + }) + } + InstrumentType::Histogram => { + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let histogram = CustomHistogramInner { + increment, + histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + histograms.push(CustomHistogram { + inner: Mutex::new(histogram), + }) } - InstrumentType::Histogram => todo!(), } } - Self { counters } + Self { + counters, + histograms, + } } } @@ -657,28 +689,33 @@ impl Instrumented for RouterCustomInstruments { fn on_request(&self, request: &Self::Request) { for counter in &self.counters { - match counter.increment { - Increment::Unit => todo!(), - Increment::Duration(_) => todo!(), - Increment::Custom(_) => todo!(), - } + counter.on_request(request); + } + for histogram in &self.histograms { + histogram.on_request(request); } } fn on_response(&self, response: &Self::Response) { - todo!() + for counter in &self.counters { + counter.on_response(response); + } + for histogram in &self.histograms { + histogram.on_response(response); + } } fn on_error(&self, error: &BoxError, ctx: &Context) { - todo!() + for counter in &self.counters { + counter.on_error(error, ctx); + } + for histogram in &self.histograms { + histogram.on_error(error, ctx); + } } } -// Une struct qui wrap counter/histogram -// Qui prend l'increment (unit/duration) -// Qui prend les attributs -// Qui implemente drop, si ça drop et que l'instrument est None donc on l'a take ça veut dire qu'il a été utilisé et donc on incremente pas au drop -// Sinon on incremente et on met un label error_code="broken_pipe" +// ---------------- Counter ----------------------- struct CustomCounter where @@ -725,7 +762,7 @@ where fn on_response(&self, response: &Self::Response) { let mut inner = self.inner.lock(); let mut attrs: Vec = inner.selectors.on_response(response).into_iter().collect(); - attrs.extend(inner.attributes); + attrs.append(&mut inner.attributes); if let Some(selected_value) = inner .selector @@ -734,7 +771,6 @@ where { inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) } - // Call actual metric macros let increment = match inner.increment { Increment::Unit => 1f64, @@ -750,10 +786,10 @@ where } } - fn on_error(&self, error: &BoxError, ctx: &Context) { + fn on_error(&self, error: &BoxError, _ctx: &Context) { let mut inner = self.inner.lock(); let mut attrs: Vec = inner.selectors.on_error(error).into_iter().collect(); - attrs.extend(inner.attributes); + attrs.append(&mut inner.attributes); // Call actual metric macros @@ -778,14 +814,15 @@ where T: Selector, { fn drop(&mut self) { + // TODO add attribute error broken pipe ? let inner = self.inner.try_lock(); - if let Some(inner) = inner { + if let Some(mut inner) = inner { if let Some(counter) = inner.counter.take() { let incr: f64 = match &inner.increment { Increment::Unit => 1f64, Increment::Duration(instant) => instant.elapsed().as_secs_f64(), Increment::Custom(val) => match val { - Some(incr) => incr as f64, + Some(incr) => *incr as f64, None => 0f64, }, }; @@ -794,3 +831,106 @@ where } } } + +// ---------------- Histogram ----------------------- + +struct CustomHistogram +where + A: Selectors + Default, + T: Selector, +{ + inner: Mutex>, +} + +struct CustomHistogramInner +where + A: Selectors + Default, + T: Selector, +{ + increment: Increment, + selector: Option>, + selectors: Extendable, + histogram: Option>, + attributes: Vec, +} + +impl Instrumented for CustomHistogram +where + A: Selectors + Default, + T: Selector, +{ + type Request = Request; + type Response = Response; + + fn on_request(&self, request: &Self::Request) { + let mut inner = self.inner.lock(); + inner.attributes = inner.selectors.on_request(request).into_iter().collect(); + if let Some(selected_value) = inner.selector.as_ref().and_then(|s| s.on_request(request)) { + inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) + } + } + + fn on_response(&self, response: &Self::Response) { + let mut inner = self.inner.lock(); + let mut attrs: Vec = inner.selectors.on_response(response).into_iter().collect(); + attrs.append(&mut inner.attributes); + + if let Some(selected_value) = inner + .selector + .as_ref() + .and_then(|s| s.on_response(response)) + { + inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) + } + + let increment = match inner.increment { + Increment::Unit => Some(1f64), + Increment::Duration(instant) => Some(instant.elapsed().as_secs_f64()), + Increment::Custom(val) => val.map(|incr| incr as f64), + }; + + if let (Some(histogram), Some(increment)) = (inner.histogram.take(), increment) { + histogram.record(increment, &attrs); + } + } + + fn on_error(&self, error: &BoxError, _ctx: &Context) { + let mut inner = self.inner.lock(); + let mut attrs: Vec = inner.selectors.on_error(error).into_iter().collect(); + attrs.append(&mut inner.attributes); + + let increment = match inner.increment { + Increment::Unit => Some(1f64), + Increment::Duration(instant) => Some(instant.elapsed().as_secs_f64()), + Increment::Custom(val) => val.map(|incr| incr as f64), + }; + + if let (Some(histogram), Some(increment)) = (inner.histogram.take(), increment) { + histogram.record(increment, &attrs); + } + } +} + +impl Drop for CustomHistogram +where + A: Selectors + Default, + T: Selector, +{ + fn drop(&mut self) { + // TODO add attribute error broken pipe ? + let inner = self.inner.try_lock(); + if let Some(mut inner) = inner { + if let Some(histogram) = inner.histogram.take() { + let increment = match &inner.increment { + Increment::Unit => Some(1f64), + Increment::Duration(instant) => Some(instant.elapsed().as_secs_f64()), + Increment::Custom(val) => val.map(|incr| incr as f64), + }; + + if let Some(increment) = increment { + histogram.record(increment, &inner.attributes); + } + } + } + } +} diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 79b045a436..82b66b7428 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -61,6 +61,7 @@ use self::config::Sampler; use self::config::SamplerOption; use self::config::TraceIdFormat; use self::config_new::instruments::Instrumented; +use self::config_new::instruments::RouterCustomInstruments; use self::config_new::spans::Spans; use self::metrics::apollo::studio::SingleTypeStat; use self::metrics::AttributesForwardConf; @@ -357,9 +358,23 @@ impl Plugin for Telemetry { ), ]); - (custom_attributes, request.context.clone()) + let custom_instruments: RouterCustomInstruments = RouterCustomInstruments::new( + &config_request.instrumentation.instruments.router.custom, + ); + custom_instruments.on_request(request); + + ( + custom_attributes, + custom_instruments, + request.context.clone(), + ) }, - move |(custom_attributes, ctx): (LinkedList, Context), fut| { + move |(custom_attributes, custom_instruments, ctx): ( + LinkedList, + RouterCustomInstruments, + Context, + ), + fut| { let start = Instant::now(); let config = config_later.clone(); @@ -390,6 +405,8 @@ impl Plugin for Telemetry { .instruments .router .on_response(response); + custom_instruments.on_response(response); + if expose_trace_id.enabled { if let Some(header_name) = &expose_trace_id.header_name { let mut headers: HashMap> = @@ -426,6 +443,7 @@ impl Plugin for Telemetry { .instruments .router .on_error(err, &ctx); + custom_instruments.on_error(err, &ctx); } response From 4b0df6af04d0d8f4485828f28a6a6d6b726fddc6 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:48:58 +0100 Subject: [PATCH 05/25] add support of supergraph and subgraph Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 242 +++++++++++++++++- apollo-router/src/plugins/telemetry/mod.rs | 39 ++- 2 files changed, 273 insertions(+), 8 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index ad3f7fdfa1..d4077650c7 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -30,6 +30,7 @@ use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::plugins::telemetry::config_new::Selectors; use crate::services::router; use crate::services::subgraph; +use crate::services::supergraph; use crate::Context; #[allow(dead_code)] @@ -43,7 +44,7 @@ pub(crate) struct InstrumentsConfig { pub(crate) router: Extendable>, /// Supergraph service instruments. For more information see documentation on Router lifecycle. - supergraph: + pub(crate) supergraph: Extendable>, /// Subgraph service instruments. For more information see documentation on Router lifecycle. pub(crate) subgraph: @@ -130,7 +131,7 @@ where #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] -struct SupergraphInstruments {} +pub(crate) struct SupergraphInstruments {} #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] @@ -715,6 +716,243 @@ impl Instrumented for RouterCustomInstruments { } } +pub(crate) struct SupergraphCustomInstruments { + counters: Vec< + CustomCounter< + supergraph::Request, + supergraph::Response, + SupergraphAttributes, + SupergraphSelector, + >, + >, + histograms: Vec< + CustomHistogram< + supergraph::Request, + supergraph::Response, + SupergraphAttributes, + SupergraphSelector, + >, + >, +} + +impl SupergraphCustomInstruments { + pub(crate) fn new( + config: &HashMap>, + ) -> Self { + let mut counters = Vec::new(); + let mut histograms = Vec::new(); + let meter = metrics::meter_provider().meter("apollo/router"); + + for (instrument_name, instrument) in config { + match instrument.ty { + InstrumentType::Counter => { + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let counter = CustomCounterInner { + increment, + counter: Some(meter.f64_counter(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + counters.push(CustomCounter { + inner: Mutex::new(counter), + }) + } + InstrumentType::Histogram => { + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let histogram = CustomHistogramInner { + increment, + histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + histograms.push(CustomHistogram { + inner: Mutex::new(histogram), + }) + } + } + } + + Self { + counters, + histograms, + } + } +} + +impl Instrumented for SupergraphCustomInstruments { + type Request = supergraph::Request; + type Response = supergraph::Response; + + fn on_request(&self, request: &Self::Request) { + for counter in &self.counters { + counter.on_request(request); + } + for histogram in &self.histograms { + histogram.on_request(request); + } + } + + fn on_response(&self, response: &Self::Response) { + for counter in &self.counters { + counter.on_response(response); + } + for histogram in &self.histograms { + histogram.on_response(response); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + for counter in &self.counters { + counter.on_error(error, ctx); + } + for histogram in &self.histograms { + histogram.on_error(error, ctx); + } + } +} + +pub(crate) struct SubgraphCustomInstruments { + counters: Vec< + CustomCounter, + >, + histograms: Vec< + CustomHistogram< + subgraph::Request, + subgraph::Response, + SubgraphAttributes, + SubgraphSelector, + >, + >, +} + +impl SubgraphCustomInstruments { + pub(crate) fn new( + config: &HashMap>, + ) -> Self { + let mut counters = Vec::new(); + let mut histograms = Vec::new(); + let meter = metrics::meter_provider().meter("apollo/router"); + + for (instrument_name, instrument) in config { + match instrument.ty { + InstrumentType::Counter => { + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let counter = CustomCounterInner { + increment, + counter: Some(meter.f64_counter(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + counters.push(CustomCounter { + inner: Mutex::new(counter), + }) + } + InstrumentType::Histogram => { + let (selector, increment) = match &instrument.value { + InstrumentValue::Standard(incr) => { + let incr = match incr { + Standard::Duration => Increment::Duration(Instant::now()), + Standard::Unit => Increment::Unit, + }; + (None, incr) + } + InstrumentValue::Custom(selector) => { + (Some(Arc::new(selector.clone())), Increment::Custom(None)) + } + }; + let histogram = CustomHistogramInner { + increment, + histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), + attributes: Vec::new(), + selector, + selectors: instrument.attributes.clone(), + }; + + histograms.push(CustomHistogram { + inner: Mutex::new(histogram), + }) + } + } + } + + Self { + counters, + histograms, + } + } +} + +impl Instrumented for SubgraphCustomInstruments { + type Request = subgraph::Request; + type Response = subgraph::Response; + + fn on_request(&self, request: &Self::Request) { + for counter in &self.counters { + counter.on_request(request); + } + for histogram in &self.histograms { + histogram.on_request(request); + } + } + + fn on_response(&self, response: &Self::Response) { + for counter in &self.counters { + counter.on_response(response); + } + for histogram in &self.histograms { + histogram.on_response(response); + } + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + for counter in &self.counters { + counter.on_error(error, ctx); + } + for histogram in &self.histograms { + histogram.on_error(error, ctx); + } + } +} + // ---------------- Counter ----------------------- struct CustomCounter diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 82b66b7428..ed67ba6b40 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -62,6 +62,8 @@ use self::config::SamplerOption; use self::config::TraceIdFormat; use self::config_new::instruments::Instrumented; use self::config_new::instruments::RouterCustomInstruments; +use self::config_new::instruments::SubgraphCustomInstruments; +use self::config_new::instruments::SupergraphCustomInstruments; use self::config_new::spans::Spans; use self::metrics::apollo::studio::SingleTypeStat; use self::metrics::AttributesForwardConf; @@ -524,9 +526,14 @@ impl Plugin for Telemetry { move |req: &SupergraphRequest| { let custom_attributes = config.instrumentation.spans.supergraph.attributes.on_request(req); Self::populate_context(config.clone(), field_level_instrumentation_ratio, req); - (req.context.clone(), custom_attributes) + let custom_instruments = SupergraphCustomInstruments::new( + &config.instrumentation.instruments.supergraph.custom, + ); + custom_instruments.on_request(req); + + (req.context.clone(), custom_instruments, custom_attributes) }, - move |(ctx, custom_attributes): (Context, LinkedList), fut| { + move |(ctx, custom_instruments, custom_attributes): (Context, SupergraphCustomInstruments, LinkedList), fut| { let config = config_map_res.clone(); let sender = metrics_sender.clone(); let start = Instant::now(); @@ -536,8 +543,14 @@ impl Plugin for Telemetry { span.set_dyn_attributes(custom_attributes); let mut result: Result = fut.await; match &result { - Ok(resp) => span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_response(resp)), - Err(err) => span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_error(err)), + Ok(resp) => { + span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_response(resp)); + custom_instruments.on_response(resp); + }, + Err(err) => { + span.set_dyn_attributes(config.instrumentation.spans.supergraph.attributes.on_error(err)); + custom_instruments.on_error(err, &ctx); + }, } result = Self::update_otel_metrics( config.clone(), @@ -613,10 +626,22 @@ impl Plugin for Telemetry { .instruments .subgraph .on_request(sub_request); + let custom_instruments = SubgraphCustomInstruments::new( + &config.instrumentation.instruments.subgraph.custom, + ); + custom_instruments.on_request(sub_request); - (sub_request.context.clone(), custom_attributes) + ( + sub_request.context.clone(), + custom_instruments, + custom_attributes, + ) }, - move |(context, custom_attributes): (Context, LinkedList), + move |(context, custom_instruments, custom_attributes): ( + Context, + SubgraphCustomInstruments, + LinkedList, + ), f: BoxFuture<'static, Result>| { let subgraph_attribute = subgraph_attribute.clone(); let subgraph_metrics_conf = subgraph_metrics_conf_resp.clone(); @@ -643,6 +668,7 @@ impl Plugin for Telemetry { .on_response(resp), ); conf.instrumentation.instruments.subgraph.on_response(resp); + custom_instruments.on_response(resp); } Err(err) => { span.record(OTEL_STATUS_CODE, "Error"); @@ -654,6 +680,7 @@ impl Plugin for Telemetry { .instruments .subgraph .on_error(err, &context); + custom_instruments.on_error(err, &context); } } From d896e2974c2d55a6fce4c2d2f45b72cca7573b9c Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:51:57 +0100 Subject: [PATCH 06/25] add support of condition Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/conditions.rs | 97 +++++- .../telemetry/config_new/instruments.rs | 296 +++--------------- 2 files changed, 140 insertions(+), 253 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index f09ebb5d59..73414098b3 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -17,11 +17,21 @@ pub(crate) enum Condition { Any(Vec>), /// The sub-condition must not be true Not(Box>), + /// Static true condition + True, + /// Static false condition + False, +} + +impl Default for Condition { + fn default() -> Self { + Self::True + } } impl Condition<()> { pub(crate) fn empty() -> Condition { - Condition::Any(vec![]) + Condition::True } } @@ -55,6 +65,89 @@ where Condition::All(all) => all.iter().all(|c| c.evaluate(request, response)), Condition::Any(any) => any.iter().any(|c| c.evaluate(request, response)), Condition::Not(not) => !not.evaluate(request, response), + Condition::True => true, + Condition::False => false, + } + } + + pub(crate) fn evaluate_request(&mut self, request: &T::Request) -> Option { + match self { + Condition::Eq(eq) => { + // We don't know if the selection was for the request or result, so we try both. + match (eq[0].on_request(request), eq[1].on_request(request)) { + (None, None) => None, + (None, Some(right)) => { + eq[1] = SelectorOrValue::Value(right.into()); + None + } + (Some(left), None) => { + eq[0] = SelectorOrValue::Value(left.into()); + None + } + (Some(left), Some(right)) => { + if left == right { + *self = Condition::True; + Some(true) + } else { + Some(false) + } + } + } + } + Condition::All(all) => { + if all.is_empty() { + return Some(true); + } + let mut response = Some(true); + for cond in all { + match cond.evaluate_request(request) { + Some(resp) => { + response = response.map(|r| resp && r); + } + None => { + response = None; + } + } + } + + response + } + Condition::Any(any) => { + if any.is_empty() { + return Some(true); + } + let mut response: Option = Some(false); + for cond in any { + match cond.evaluate_request(request) { + Some(resp) => { + response = response.map(|r| resp || r); + } + None => { + response = None; + } + } + } + + response + } + Condition::Not(not) => not.evaluate_request(request).map(|r| !r), + Condition::True => Some(true), + Condition::False => Some(false), + } + } + + pub(crate) fn evaluate_response(&self, response: &T::Response) -> bool { + match self { + Condition::Eq(eq) => { + let left = eq[0].on_response(response); + let right = eq[1].on_response(response); + left == right + } + Condition::All(all) => all.iter().all(|c| c.evaluate_response(response)), + Condition::Any(any) => any.iter().any(|c| c.evaluate_response(response)), + Condition::Not(not) => !not.evaluate_response(response), + Condition::True => true, + Condition::False => false, } } } @@ -69,6 +162,7 @@ where fn on_request(&self, request: &T::Request) -> Option { match self { SelectorOrValue::Value(value) => Some(value.clone().into()), + // TODO return Some(null) ?! SelectorOrValue::Selector(selector) => selector.on_request(request), } } @@ -76,6 +170,7 @@ where fn on_response(&self, response: &T::Response) -> Option { match self { SelectorOrValue::Value(value) => Some(value.clone().into()), + // TODO return Some(null) ?! SelectorOrValue::Selector(selector) => selector.on_response(response), } } diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index d4077650c7..7543c06798 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -607,138 +607,21 @@ impl Selectors for SubgraphInstrumentsConfig { } } -pub(crate) struct RouterCustomInstruments { - counters: - Vec>, - histograms: - Vec>, -} - -impl RouterCustomInstruments { - pub(crate) fn new( - config: &HashMap>, - ) -> Self { - let mut counters = Vec::new(); - let mut histograms = Vec::new(); - let meter = metrics::meter_provider().meter("apollo/router"); - - for (instrument_name, instrument) in config { - match instrument.ty { - InstrumentType::Counter => { - let (selector, increment) = match &instrument.value { - InstrumentValue::Standard(incr) => { - let incr = match incr { - Standard::Duration => Increment::Duration(Instant::now()), - Standard::Unit => Increment::Unit, - }; - (None, incr) - } - InstrumentValue::Custom(selector) => { - (Some(Arc::new(selector.clone())), Increment::Custom(None)) - } - }; - let counter = CustomCounterInner { - increment, - counter: Some(meter.f64_counter(instrument_name.clone()).init()), - attributes: Vec::new(), - selector, - selectors: instrument.attributes.clone(), - }; - - counters.push(CustomCounter { - inner: Mutex::new(counter), - }) - } - InstrumentType::Histogram => { - let (selector, increment) = match &instrument.value { - InstrumentValue::Standard(incr) => { - let incr = match incr { - Standard::Duration => Increment::Duration(Instant::now()), - Standard::Unit => Increment::Unit, - }; - (None, incr) - } - InstrumentValue::Custom(selector) => { - (Some(Arc::new(selector.clone())), Increment::Custom(None)) - } - }; - let histogram = CustomHistogramInner { - increment, - histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), - attributes: Vec::new(), - selector, - selectors: instrument.attributes.clone(), - }; - - histograms.push(CustomHistogram { - inner: Mutex::new(histogram), - }) - } - } - } - - Self { - counters, - histograms, - } - } -} - -impl Instrumented for RouterCustomInstruments { - type Request = router::Request; - type Response = router::Response; - - fn on_request(&self, request: &Self::Request) { - for counter in &self.counters { - counter.on_request(request); - } - for histogram in &self.histograms { - histogram.on_request(request); - } - } - - fn on_response(&self, response: &Self::Response) { - for counter in &self.counters { - counter.on_response(response); - } - for histogram in &self.histograms { - histogram.on_response(response); - } - } - - fn on_error(&self, error: &BoxError, ctx: &Context) { - for counter in &self.counters { - counter.on_error(error, ctx); - } - for histogram in &self.histograms { - histogram.on_error(error, ctx); - } - } -} - -pub(crate) struct SupergraphCustomInstruments { - counters: Vec< - CustomCounter< - supergraph::Request, - supergraph::Response, - SupergraphAttributes, - SupergraphSelector, - >, - >, - histograms: Vec< - CustomHistogram< - supergraph::Request, - supergraph::Response, - SupergraphAttributes, - SupergraphSelector, - >, - >, +pub(crate) struct CustomInstruments +where + Attributes: Selectors + Default, + Select: Selector + Debug, +{ + counters: Vec>, + histograms: Vec>, } -impl SupergraphCustomInstruments { - pub(crate) fn new( - config: &HashMap>, - ) -> Self { +impl CustomInstruments +where + Attributes: Selectors + Default + Debug + Clone, + Select: Selector + Debug + Clone, +{ + pub(crate) fn new(config: &HashMap>) -> Self { let mut counters = Vec::new(); let mut histograms = Vec::new(); let meter = metrics::meter_provider().meter("apollo/router"); @@ -760,6 +643,7 @@ impl SupergraphCustomInstruments { }; let counter = CustomCounterInner { increment, + condition: instrument.condition.clone(), counter: Some(meter.f64_counter(instrument_name.clone()).init()), attributes: Vec::new(), selector, @@ -805,9 +689,14 @@ impl SupergraphCustomInstruments { } } -impl Instrumented for SupergraphCustomInstruments { - type Request = supergraph::Request; - type Response = supergraph::Response; +impl Instrumented + for CustomInstruments +where + Attributes: Selectors + Default, + Select: Selector + Debug, +{ + type Request = Request; + type Response = Response; fn on_request(&self, request: &Self::Request) { for counter in &self.counters { @@ -837,128 +726,25 @@ impl Instrumented for SupergraphCustomInstruments { } } -pub(crate) struct SubgraphCustomInstruments { - counters: Vec< - CustomCounter, - >, - histograms: Vec< - CustomHistogram< - subgraph::Request, - subgraph::Response, - SubgraphAttributes, - SubgraphSelector, - >, - >, -} +pub(crate) type RouterCustomInstruments = + CustomInstruments; -impl SubgraphCustomInstruments { - pub(crate) fn new( - config: &HashMap>, - ) -> Self { - let mut counters = Vec::new(); - let mut histograms = Vec::new(); - let meter = metrics::meter_provider().meter("apollo/router"); +pub(crate) type SupergraphCustomInstruments = CustomInstruments< + supergraph::Request, + supergraph::Response, + SupergraphAttributes, + SupergraphSelector, +>; - for (instrument_name, instrument) in config { - match instrument.ty { - InstrumentType::Counter => { - let (selector, increment) = match &instrument.value { - InstrumentValue::Standard(incr) => { - let incr = match incr { - Standard::Duration => Increment::Duration(Instant::now()), - Standard::Unit => Increment::Unit, - }; - (None, incr) - } - InstrumentValue::Custom(selector) => { - (Some(Arc::new(selector.clone())), Increment::Custom(None)) - } - }; - let counter = CustomCounterInner { - increment, - counter: Some(meter.f64_counter(instrument_name.clone()).init()), - attributes: Vec::new(), - selector, - selectors: instrument.attributes.clone(), - }; - - counters.push(CustomCounter { - inner: Mutex::new(counter), - }) - } - InstrumentType::Histogram => { - let (selector, increment) = match &instrument.value { - InstrumentValue::Standard(incr) => { - let incr = match incr { - Standard::Duration => Increment::Duration(Instant::now()), - Standard::Unit => Increment::Unit, - }; - (None, incr) - } - InstrumentValue::Custom(selector) => { - (Some(Arc::new(selector.clone())), Increment::Custom(None)) - } - }; - let histogram = CustomHistogramInner { - increment, - histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), - attributes: Vec::new(), - selector, - selectors: instrument.attributes.clone(), - }; - - histograms.push(CustomHistogram { - inner: Mutex::new(histogram), - }) - } - } - } - - Self { - counters, - histograms, - } - } -} - -impl Instrumented for SubgraphCustomInstruments { - type Request = subgraph::Request; - type Response = subgraph::Response; - - fn on_request(&self, request: &Self::Request) { - for counter in &self.counters { - counter.on_request(request); - } - for histogram in &self.histograms { - histogram.on_request(request); - } - } - - fn on_response(&self, response: &Self::Response) { - for counter in &self.counters { - counter.on_response(response); - } - for histogram in &self.histograms { - histogram.on_response(response); - } - } - - fn on_error(&self, error: &BoxError, ctx: &Context) { - for counter in &self.counters { - counter.on_error(error, ctx); - } - for histogram in &self.histograms { - histogram.on_error(error, ctx); - } - } -} +pub(crate) type SubgraphCustomInstruments = + CustomInstruments; // ---------------- Counter ----------------------- struct CustomCounter where A: Selectors + Default, - T: Selector, + T: Selector + Debug, { inner: Mutex>, } @@ -966,12 +752,13 @@ where struct CustomCounterInner where A: Selectors + Default, - T: Selector, + T: Selector + Debug, { increment: Increment, selector: Option>, selectors: Extendable, counter: Option>, + condition: Condition, attributes: Vec, } @@ -984,13 +771,16 @@ enum Increment { impl Instrumented for CustomCounter where A: Selectors + Default, - T: Selector, + T: Selector + Debug + Debug, { type Request = Request; type Response = Response; fn on_request(&self, request: &Self::Request) { let mut inner = self.inner.lock(); + if inner.condition.evaluate_request(request) == Some(false) { + return; + } inner.attributes = inner.selectors.on_request(request).into_iter().collect(); if let Some(selected_value) = inner.selector.as_ref().and_then(|s| s.on_request(request)) { inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) @@ -999,6 +789,10 @@ where fn on_response(&self, response: &Self::Response) { let mut inner = self.inner.lock(); + if !inner.condition.evaluate_response(response) { + let _ = inner.counter.take(); + return; + } let mut attrs: Vec = inner.selectors.on_response(response).into_iter().collect(); attrs.append(&mut inner.attributes); @@ -1029,8 +823,6 @@ where let mut attrs: Vec = inner.selectors.on_error(error).into_iter().collect(); attrs.append(&mut inner.attributes); - // Call actual metric macros - let increment = match inner.increment { Increment::Unit => 1f64, Increment::Duration(instant) => instant.elapsed().as_secs_f64(), @@ -1049,7 +841,7 @@ where impl Drop for CustomCounter where A: Selectors + Default, - T: Selector, + T: Selector + Debug, { fn drop(&mut self) { // TODO add attribute error broken pipe ? From d7ae2f28a2f32019bbe2c62f22d1d3dce3e75529 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 3 Mar 2024 21:43:27 +0100 Subject: [PATCH 07/25] add exist condition Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/conditions.rs | 221 +++++++++++++----- 1 file changed, 163 insertions(+), 58 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index 73414098b3..d034d9178d 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -11,6 +11,8 @@ use crate::plugins::telemetry::config_new::Selector; pub(crate) enum Condition { /// A condition to check a selection against a value. Eq([SelectorOrValue; 2]), + /// A condition to check a selection against a selector. + Exist(T), /// All sub-conditions must be true. All(Vec>), /// At least one sub-conditions must be true. @@ -50,52 +52,38 @@ impl Condition where T: Selector, { - fn evaluate(&self, request: &T::Request, response: &T::Response) -> bool { - match self { - Condition::Eq(eq) => { - // We don't know if the selection was for the request or result, so we try both. - let left = eq[0] - .on_request(request) - .or_else(|| eq[0].on_response(response)); - let right = eq[1] - .on_request(request) - .or_else(|| eq[1].on_response(response)); - left == right - } - Condition::All(all) => all.iter().all(|c| c.evaluate(request, response)), - Condition::Any(any) => any.iter().any(|c| c.evaluate(request, response)), - Condition::Not(not) => !not.evaluate(request, response), - Condition::True => true, - Condition::False => false, - } - } - pub(crate) fn evaluate_request(&mut self, request: &T::Request) -> Option { match self { - Condition::Eq(eq) => { - // We don't know if the selection was for the request or result, so we try both. - match (eq[0].on_request(request), eq[1].on_request(request)) { - (None, None) => None, - (None, Some(right)) => { - eq[1] = SelectorOrValue::Value(right.into()); - None - } - (Some(left), None) => { - eq[0] = SelectorOrValue::Value(left.into()); - None - } - (Some(left), Some(right)) => { - if left == right { - *self = Condition::True; - Some(true) - } else { - Some(false) - } + Condition::Eq(eq) => match (eq[0].on_request(request), eq[1].on_request(request)) { + (None, None) => None, + (None, Some(right)) => { + eq[1] = SelectorOrValue::Value(right.into()); + None + } + (Some(left), None) => { + eq[0] = SelectorOrValue::Value(left.into()); + None + } + (Some(left), Some(right)) => { + if left == right { + *self = Condition::True; + Some(true) + } else { + Some(false) } } + }, + Condition::Exist(exist) => { + if exist.on_request(request).is_some() { + *self = Condition::True; + Some(true) + } else { + None + } } Condition::All(all) => { if all.is_empty() { + *self = Condition::True; return Some(true); } let mut response = Some(true); @@ -114,6 +102,7 @@ where } Condition::Any(any) => { if any.is_empty() { + *self = Condition::True; return Some(true); } let mut response: Option = Some(false); @@ -143,6 +132,7 @@ where let right = eq[1].on_response(response); left == right } + Condition::Exist(exist) => exist.on_response(response).is_some(), Condition::All(all) => all.iter().all(|c| c.evaluate_response(response)), Condition::Any(any) => any.iter().any(|c| c.evaluate_response(response)), Condition::Not(not) => !not.evaluate_response(response), @@ -198,54 +188,126 @@ mod test { } } + enum TestSelectorReqRes { + Req, + Resp, + } + + impl Selector for TestSelectorReqRes { + type Request = Option; + type Response = Option; + + fn on_request(&self, request: &Self::Request) -> Option { + match self { + TestSelectorReqRes::Req => request.map(Value::I64), + TestSelectorReqRes::Resp => None, + } + } + + fn on_response(&self, response: &Self::Response) -> Option { + match self { + TestSelectorReqRes::Req => None, + TestSelectorReqRes::Resp => response.map(Value::I64), + } + } + } + + #[test] + fn test_condition_exist() { + assert_eq!( + None, + Condition::::Exist(TestSelectorReqRes::Req).evaluate_request(&None) + ); + assert!( + !Condition::::Exist(TestSelectorReqRes::Req) + .evaluate_response(&Some(3i64)) + ); + assert_eq!( + Some(true), + Condition::::Exist(TestSelectorReqRes::Req) + .evaluate_request(&Some(2i64)) + ); + assert!( + Condition::::Exist(TestSelectorReqRes::Resp) + .evaluate_response(&Some(3i64)) + ); + } + #[test] fn test_condition_eq() { + assert_eq!( + Some(true), + Condition::::Eq([ + SelectorOrValue::Value(1i64.into()), + SelectorOrValue::Value(1i64.into()), + ]) + .evaluate_request(&None) + ); assert!(Condition::::Eq([ SelectorOrValue::Value(1i64.into()), SelectorOrValue::Value(1i64.into()), ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); assert!(!Condition::::Eq([ SelectorOrValue::Value(1i64.into()), SelectorOrValue::Value(2i64.into()), ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); } #[test] fn test_condition_eq_selector() { + assert_eq!( + Some(true), + Condition::::Eq([ + SelectorOrValue::Selector(TestSelector), + SelectorOrValue::Value(1i64.into()), + ]) + .evaluate_request(&Some(1i64)) + ); + assert_eq!( + Some(true), + Condition::::Eq([ + SelectorOrValue::Value(1i64.into()), + SelectorOrValue::Selector(TestSelector), + ]) + .evaluate_request(&Some(1i64)) + ); + assert!(Condition::::Eq([ SelectorOrValue::Selector(TestSelector), - SelectorOrValue::Value(1i64.into()), - ]) - .evaluate(&Some(1i64), &None)); - assert!(Condition::::Eq([ - SelectorOrValue::Value(1i64.into()), - SelectorOrValue::Selector(TestSelector), + SelectorOrValue::Value(2i64.into()), ]) - .evaluate(&Some(1i64), &None)); + .evaluate_request(&None) + .is_none()); assert!(Condition::::Eq([ SelectorOrValue::Selector(TestSelector), SelectorOrValue::Value(2i64.into()), ]) - .evaluate(&None, &Some(2i64))); + .evaluate_response(&Some(2i64))); assert!(Condition::::Eq([ SelectorOrValue::Value(2i64.into()), SelectorOrValue::Selector(TestSelector), ]) - .evaluate(&None, &Some(2i64))); + .evaluate_response(&Some(2i64))); assert!(!Condition::::Eq([ SelectorOrValue::Selector(TestSelector), SelectorOrValue::Value(3i64.into()), ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); assert!(!Condition::::Eq([ SelectorOrValue::Value(3i64.into()), SelectorOrValue::Selector(TestSelector), ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); + + let mut condition = Condition::::Eq([ + SelectorOrValue::Value(3i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Req), + ]); + assert_eq!(Some(false), condition.evaluate_request(&Some(2i64))); } #[test] @@ -254,13 +316,13 @@ mod test { SelectorOrValue::Value(1i64.into()), SelectorOrValue::Value(2i64.into()), ]))) - .evaluate(&None, &None)); + .evaluate_response(&None)); assert!(!Condition::::Not(Box::new(Condition::Eq([ SelectorOrValue::Value(1i64.into()), SelectorOrValue::Value(1i64.into()), ]))) - .evaluate(&None, &None)); + .evaluate_response(&None)); } #[test] @@ -275,7 +337,49 @@ mod test { SelectorOrValue::Value(2i64.into()), ]) ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); + + let mut condition = Condition::::All(vec![ + Condition::Eq([ + SelectorOrValue::Value(1i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Req), + ]), + Condition::Eq([ + SelectorOrValue::Value(3i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Resp), + ]), + ]); + + assert!(condition.evaluate_request(&Some(1i64)).is_none()); + assert!(condition.evaluate_response(&Some(3i64))); + + let mut condition = Condition::::All(vec![ + Condition::Eq([ + SelectorOrValue::Value(1i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Req), + ]), + Condition::Eq([ + SelectorOrValue::Value(3i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Resp), + ]), + ]); + + assert!(condition.evaluate_request(&Some(1i64)).is_none()); + assert!(!condition.evaluate_response(&Some(2i64))); + + let mut condition = Condition::::All(vec![ + Condition::Eq([ + SelectorOrValue::Value(1i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Req), + ]), + Condition::Eq([ + SelectorOrValue::Value(3i64.into()), + SelectorOrValue::Selector(TestSelectorReqRes::Req), + ]), + ]); + + assert_eq!(Some(false), condition.evaluate_request(&Some(1i64))); + assert!(!condition.evaluate_response(&Some(2i64))); assert!(!Condition::::All(vec![ Condition::Eq([ @@ -287,8 +391,9 @@ mod test { SelectorOrValue::Value(2i64.into()), ]) ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); } + #[test] fn test_condition_any() { assert!(Condition::::Any(vec![ @@ -301,7 +406,7 @@ mod test { SelectorOrValue::Value(2i64.into()), ]) ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); assert!(!Condition::::All(vec![ Condition::Eq([ @@ -313,6 +418,6 @@ mod test { SelectorOrValue::Value(2i64.into()), ]) ]) - .evaluate(&None, &None)); + .evaluate_response(&None)); } } From 215cd19dfcde0fe80bf273d04755a404018c4f34 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:31:04 +0100 Subject: [PATCH 08/25] add test for instruments Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/metrics/aggregation.rs | 1 - apollo-router/src/metrics/mod.rs | 24 +- .../telemetry/config_new/attributes.rs | 3 +- .../telemetry/config_new/instruments.rs | 635 ++++++++++++------ .../plugins/telemetry/config_new/selectors.rs | 8 + apollo-router/src/plugins/telemetry/mod.rs | 331 ++++++++- .../testdata/custom_instruments.router.yaml | 76 +++ 7 files changed, 827 insertions(+), 251 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml diff --git a/apollo-router/src/metrics/aggregation.rs b/apollo-router/src/metrics/aggregation.rs index 020ce1532d..5f23e7f182 100644 --- a/apollo-router/src/metrics/aggregation.rs +++ b/apollo-router/src/metrics/aggregation.rs @@ -56,7 +56,6 @@ impl Default for AggregateMeterProvider { inner: Arc::new(Mutex::new(Inner::default())), }; - dbg!("default aggregate meter provider"); // If the regular global meter provider has been set then the aggregate meter provider will use it. Otherwise it'll default to a no-op. // For this to work the global meter provider must be set before the aggregate meter provider is created. // This functionality is not guaranteed to stay like this, so use at your own risk. diff --git a/apollo-router/src/metrics/mod.rs b/apollo-router/src/metrics/mod.rs index a65617c0a3..5c72fdb879 100644 --- a/apollo-router/src/metrics/mod.rs +++ b/apollo-router/src/metrics/mod.rs @@ -231,11 +231,9 @@ pub(crate) mod test_utils { } else if let Some(histogram) = metric.data.as_any().downcast_ref::>() { if matches!(ty, MetricType::Histogram) { - if let Some(value) = value.to_u64() { - return histogram.data_points.iter().any(|datapoint| { - datapoint.attributes == *attributes && datapoint.count == value - }); - } + return histogram.data_points.iter().any(|datapoint| { + datapoint.attributes == *attributes && datapoint.sum == value + }); } } } @@ -905,7 +903,7 @@ macro_rules! assert_gauge { } #[cfg(test)] -macro_rules! assert_histogram { +macro_rules! assert_histogram_sum { ($($name:ident).+, $value: expr, $($attr_key:literal = $attr_value:expr),+) => { let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; @@ -932,7 +930,7 @@ macro_rules! assert_histogram { }; ($name:literal, $value: expr) => { - let result = crate::metrics::collect_metrics().assert($name, $value, &[]); + let result = crate::metrics::collect_metrics().assert($name, crate::metrics::test_utils::MetricType::Histogram, $value, &[]); assert_metric!(result, $name, None, Some($value.into()), &[]); }; } @@ -1143,7 +1141,7 @@ mod test { async fn test_u64_histogram() { async { u64_histogram!("test", "test description", 1, "attr" = "val"); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; @@ -1153,7 +1151,7 @@ mod test { async fn test_i64_histogram() { async { i64_histogram!("test", "test description", 1, "attr" = "val"); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; @@ -1163,7 +1161,7 @@ mod test { async fn test_f64_histogram() { async { f64_histogram!("test", "test description", 1.0, "attr" = "val"); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; @@ -1185,7 +1183,7 @@ mod test { async fn test_type_counter() { async { f64_counter!("test", "test description", 1.0, "attr" = "val"); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; @@ -1196,7 +1194,7 @@ mod test { async fn test_type_up_down_counter() { async { f64_up_down_counter!("test", "test description", 1.0, "attr" = "val"); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; @@ -1211,7 +1209,7 @@ mod test { .u64_observable_gauge("test") .with_callback(|m| m.observe(5, &[])) .init(); - assert_histogram!("test", 1, "attr" = "val"); + assert_histogram_sum!("test", 1, "attr" = "val"); } .with_metrics() .await; diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index be3dc29216..b3b62d304f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -647,6 +647,7 @@ impl Selectors for HttpCommonAttributes { )); } } + if let Some(true) = &self.http_response_status_code { attrs.push_back(KeyValue::new( HTTP_RESPONSE_STATUS_CODE, @@ -848,7 +849,7 @@ impl HttpServerAttributes { .next() } - fn forwarded_host(request: &Request) -> Option { + pub(crate) fn forwarded_host(request: &Request) -> Option { request .router_request .headers() diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 7543c06798..c7056585a8 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -10,12 +10,17 @@ use opentelemetry_api::metrics::MeterProvider; use opentelemetry_api::metrics::Unit; use opentelemetry_api::metrics::UpDownCounter; use opentelemetry_api::KeyValue; +use opentelemetry_semantic_conventions::trace::HTTP_REQUEST_METHOD; +use opentelemetry_semantic_conventions::trace::SERVER_ADDRESS; +use opentelemetry_semantic_conventions::trace::SERVER_PORT; +use opentelemetry_semantic_conventions::trace::URL_SCHEME; use parking_lot::Mutex; use schemars::JsonSchema; use serde::Deserialize; use tokio::time::Instant; use tower::BoxError; +use super::attributes::HttpServerAttributes; use super::Selector; use crate::metrics; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; @@ -51,7 +56,180 @@ pub(crate) struct InstrumentsConfig { Extendable>, } -#[allow(dead_code)] +impl InstrumentsConfig { + pub(crate) fn new_router_instruments(&self) -> RouterInstruments { + let meter = metrics::meter_provider().meter("apollo/router"); + let http_server_request_duration = self + .router + .attributes + .http_server_request_duration + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Duration(Instant::now()), + histogram: Some(meter.f64_histogram("http.server.request.duration").init()), + attributes: Vec::new(), + selector: None, + selectors: match &self.router.attributes.http_server_request_duration { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + let http_server_request_body_size = self + .router + .attributes + .http_server_request_body_size + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some(meter.f64_histogram("http.server.request.body.size").init()), + attributes: Vec::new(), + selector: Some(Arc::new(RouterSelector::RequestHeader { + request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors: match &self.router.attributes.http_server_request_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + let http_server_response_body_size = self + .router + .attributes + .http_server_response_body_size + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some(meter.f64_histogram("http.server.response.body.size").init()), + attributes: Vec::new(), + selector: Some(Arc::new(RouterSelector::ResponseHeader { + response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors: match &self.router.attributes.http_server_response_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + let http_server_active_requests = self + .router + .attributes + .http_server_active_requests + .is_enabled() + .then(|| ActiveRequestsCounter { + inner: Mutex::new(ActiveRequestsCounterInner { + counter: Some( + meter + .i64_up_down_counter("http.server.active_requests") + .init(), + ), + attrs_config: match &self.router.attributes.http_server_active_requests { + DefaultedStandardInstrument::Bool(_) => Default::default(), + DefaultedStandardInstrument::Extendable { attributes } => { + attributes.clone() + } + }, + attributes: Vec::new(), + }), + }); + RouterInstruments { + http_server_request_duration, + http_server_request_body_size, + http_server_response_body_size, + http_server_active_requests, + custom: CustomInstruments::new(&self.router.custom), + } + } + + pub(crate) fn new_subgraph_instruments(&self) -> SubgraphInstruments { + let meter = metrics::meter_provider().meter("apollo/router"); + let http_client_request_duration = self + .subgraph + .attributes + .http_client_request_duration + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Duration(Instant::now()), + histogram: Some(meter.f64_histogram("http.client.request.duration").init()), + attributes: Vec::new(), + selector: None, + selectors: match &self.subgraph.attributes.http_client_request_duration { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + let http_client_request_body_size = self + .subgraph + .attributes + .http_client_request_body_size + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some(meter.f64_histogram("http.client.request.body.size").init()), + attributes: Vec::new(), + selector: Some(Arc::new(SubgraphSelector::SubgraphRequestHeader { + subgraph_request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors: match &self.subgraph.attributes.http_client_request_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + let http_client_response_body_size = self + .subgraph + .attributes + .http_client_response_body_size + .is_enabled() + .then(|| CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some(meter.f64_histogram("http.client.response.body.size").init()), + attributes: Vec::new(), + selector: Some(Arc::new(SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors: match &self.subgraph.attributes.http_client_response_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + Some(attributes.clone()) + } + }, + }), + }); + SubgraphInstruments { + http_client_request_duration, + http_client_request_body_size, + http_client_response_body_size, + custom: CustomInstruments::new(&self.subgraph.custom), + } + } +} + #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] pub(crate) struct RouterInstrumentsConfig { @@ -60,10 +238,9 @@ pub(crate) struct RouterInstrumentsConfig { http_server_request_duration: DefaultedStandardInstrument>, - /// Gauge of active requests + /// Counter of active requests #[serde(rename = "http.server.active_requests")] - http_server_active_requests: - DefaultedStandardInstrument>, + http_server_active_requests: DefaultedStandardInstrument, /// Histogram of server request body size #[serde(rename = "http.server.request.body.size")] @@ -76,6 +253,15 @@ pub(crate) struct RouterInstrumentsConfig { DefaultedStandardInstrument>, } +#[derive(Clone, Deserialize, JsonSchema, Debug, Default)] +#[serde(deny_unknown_fields, default)] +pub(crate) struct ActiveRequestsAttributes { + http_request_method: bool, + server_address: bool, + server_port: bool, + url_scheme: bool, +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug)] #[serde(deny_unknown_fields, untagged)] @@ -236,21 +422,6 @@ pub(crate) enum Standard { // Active, } -struct ActiveRequestGuard(UpDownCounter, Vec); - -impl ActiveRequestGuard { - fn new(counter: UpDownCounter, attrs: Vec) -> Self { - counter.add(1, &attrs); - Self(counter, attrs) - } -} - -impl Drop for ActiveRequestGuard { - fn drop(&mut self) { - self.0.add(-1, &self.1); - } -} - pub(crate) trait Instrumented { type Request; type Response; @@ -260,142 +431,6 @@ pub(crate) trait Instrumented { fn on_error(&self, error: &BoxError, ctx: &Context); } -pub(crate) trait InstrumentedMut { - type Request; - type Response; - - fn on_request(&mut self, request: &Self::Request); - fn on_response(&mut self, response: &Self::Response); - fn on_error(&mut self, error: &BoxError, ctx: &Context); -} - -impl Instrumented for RouterInstrumentsConfig { - type Request = router::Request; - type Response = router::Response; - - fn on_request(&self, request: &Self::Request) { - let meter = metrics::meter_provider().meter("apollo/router"); - if self.http_server_active_requests.is_enabled() { - let attrs = self - .http_server_active_requests - .on_request(request) - .into_iter() - .collect::>(); - let active_req_guard = ActiveRequestGuard::new( - meter - .i64_up_down_counter("http.server.active_requests") - .init(), - attrs, - ); - request.context.extensions().lock().insert(active_req_guard); - } - - if self.http_server_request_body_size.is_enabled() { - let body_size = request - .router_request - .headers() - .get(&CONTENT_LENGTH) - .and_then(|val| val.to_str().ok()?.parse::().ok()); - if let Some(body_size) = body_size { - match meter - .u64_histogram("http.server.request.body.size") - .try_init() - { - Ok(histogram) => { - let attrs = self - .http_server_request_body_size - .on_request(request) - .into_iter() - .collect::>(); - histogram.record(body_size, &attrs); - } - Err(err) => { - tracing::error!( - "cannot create histogram for 'http.server.request.body.size': {err:?}" - ); - } - } - } - } - } - - fn on_response(&self, response: &Self::Response) { - let meter = metrics::meter_provider().meter("apollo/router"); - if self.http_server_request_duration.is_enabled() { - let attrs = self - .http_server_request_duration - .on_response(response) - .into_iter() - .collect::>(); - let request_duration = response.context.busy_time(); - match meter - .f64_histogram("http.server.request.duration") - .with_unit(Unit::new("s")) - .try_init() - { - Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - Err(err) => { - tracing::error!( - "cannot create gauge for 'http.server.request.duration': {err:?}" - ); - } - } - } - - if self.http_server_response_body_size.is_enabled() { - let body_size = response - .response - .headers() - .get(&CONTENT_LENGTH) - .and_then(|val| val.to_str().ok()?.parse::().ok()); - if let Some(body_size) = body_size { - match meter - .u64_histogram("http.server.response.body.size") - .try_init() - { - Ok(histogram) => { - let attrs = self - .http_server_response_body_size - .on_response(response) - .into_iter() - .collect::>(); - histogram.record(body_size, &attrs); - } - Err(err) => { - tracing::error!( - "cannot create gauge for 'http.server.response.body.size': {err:?}" - ); - } - } - } - } - } - - fn on_error(&self, error: &BoxError, ctx: &Context) { - let meter = metrics::meter_provider().meter("apollo/router"); - if self.http_server_request_duration.is_enabled() { - let attrs = self - .http_server_request_duration - .on_error(error) - .into_iter() - .collect::>(); - let request_duration = ctx.busy_time(); - match meter - .f64_histogram("http.server.request.duration") - .with_unit(Unit::new("s")) - .try_init() - { - Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - Err(err) => { - tracing::error!( - "cannot create gauge for 'http.server.request.duration': {err:?}" - ); - } - } - } - } -} - struct SubgraphInstant(Instant); impl Instrumented for SubgraphInstrumentsConfig { @@ -511,13 +546,12 @@ impl Instrumented for SubgraphInstrumentsConfig { .get::() .map(|i| i.0.elapsed()); if let Some(request_duration) = request_duration { - match meter + if let Ok(histogram) = meter .f64_histogram("http.client.request.duration") .with_unit(Unit::new("s")) .try_init() { - Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - Err(_) => todo!(), + histogram.record(request_duration.as_secs_f64(), &attrs) } } } @@ -546,38 +580,6 @@ where } } -impl Selectors for RouterInstrumentsConfig { - type Request = router::Request; - type Response = router::Response; - - fn on_request(&self, request: &Self::Request) -> LinkedList { - let mut attrs = self.http_server_active_requests.on_request(request); - attrs.extend(self.http_server_request_body_size.on_request(request)); - attrs.extend(self.http_server_request_duration.on_request(request)); - attrs.extend(self.http_server_response_body_size.on_request(request)); - - attrs - } - - fn on_response(&self, response: &Self::Response) -> LinkedList { - let mut attrs = self.http_server_active_requests.on_response(response); - attrs.extend(self.http_server_request_body_size.on_response(response)); - attrs.extend(self.http_server_request_duration.on_response(response)); - attrs.extend(self.http_server_response_body_size.on_response(response)); - - attrs - } - - fn on_error(&self, error: &BoxError) -> LinkedList { - let mut attrs = self.http_server_active_requests.on_error(error); - attrs.extend(self.http_server_request_body_size.on_error(error)); - attrs.extend(self.http_server_request_duration.on_error(error)); - attrs.extend(self.http_server_response_body_size.on_error(error)); - - attrs - } -} - impl Selectors for SubgraphInstrumentsConfig { type Request = subgraph::Request; type Response = subgraph::Response; @@ -672,7 +674,7 @@ where histogram: Some(meter.f64_histogram(instrument_name.clone()).init()), attributes: Vec::new(), selector, - selectors: instrument.attributes.clone(), + selectors: Some(instrument.attributes.clone()), }; histograms.push(CustomHistogram { @@ -726,6 +728,147 @@ where } } +pub(crate) struct RouterInstruments { + http_server_request_duration: Option< + CustomHistogram, + >, + http_server_active_requests: Option, + http_server_request_body_size: Option< + CustomHistogram, + >, + http_server_response_body_size: Option< + CustomHistogram, + >, + custom: RouterCustomInstruments, +} + +impl Instrumented for RouterInstruments { + type Request = router::Request; + + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) { + if let Some(http_server_request_duration) = &self.http_server_request_duration { + http_server_request_duration.on_request(request); + } + if let Some(http_server_active_requests) = &self.http_server_active_requests { + http_server_active_requests.on_request(request); + } + if let Some(http_server_request_body_size) = &self.http_server_request_body_size { + http_server_request_body_size.on_request(request); + } + if let Some(http_server_response_body_size) = &self.http_server_response_body_size { + http_server_response_body_size.on_request(request); + } + self.custom.on_request(request); + } + + fn on_response(&self, response: &Self::Response) { + if let Some(http_server_request_duration) = &self.http_server_request_duration { + http_server_request_duration.on_response(response); + } + if let Some(http_server_active_requests) = &self.http_server_active_requests { + http_server_active_requests.on_response(response); + } + if let Some(http_server_request_body_size) = &self.http_server_request_body_size { + http_server_request_body_size.on_response(response); + } + if let Some(http_server_response_body_size) = &self.http_server_response_body_size { + http_server_response_body_size.on_response(response); + } + self.custom.on_response(response); + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + if let Some(http_server_request_duration) = &self.http_server_request_duration { + http_server_request_duration.on_error(error, ctx); + } + if let Some(http_server_active_requests) = &self.http_server_active_requests { + http_server_active_requests.on_error(error, ctx); + } + if let Some(http_server_request_body_size) = &self.http_server_request_body_size { + http_server_request_body_size.on_error(error, ctx); + } + if let Some(http_server_response_body_size) = &self.http_server_response_body_size { + http_server_response_body_size.on_error(error, ctx); + } + self.custom.on_error(error, ctx); + } +} + +pub(crate) struct SubgraphInstruments { + http_client_request_duration: Option< + CustomHistogram< + subgraph::Request, + subgraph::Response, + SubgraphAttributes, + SubgraphSelector, + >, + >, + http_client_request_body_size: Option< + CustomHistogram< + subgraph::Request, + subgraph::Response, + SubgraphAttributes, + SubgraphSelector, + >, + >, + http_client_response_body_size: Option< + CustomHistogram< + subgraph::Request, + subgraph::Response, + SubgraphAttributes, + SubgraphSelector, + >, + >, + custom: SubgraphCustomInstruments, +} + +impl Instrumented for SubgraphInstruments { + type Request = subgraph::Request; + + type Response = subgraph::Response; + + fn on_request(&self, request: &Self::Request) { + if let Some(http_client_request_duration) = &self.http_client_request_duration { + http_client_request_duration.on_request(request); + } + if let Some(http_client_request_body_size) = &self.http_client_request_body_size { + http_client_request_body_size.on_request(request); + } + if let Some(http_client_response_body_size) = &self.http_client_response_body_size { + http_client_response_body_size.on_request(request); + } + self.custom.on_request(request); + } + + fn on_response(&self, response: &Self::Response) { + if let Some(http_client_request_duration) = &self.http_client_request_duration { + http_client_request_duration.on_response(response); + } + if let Some(http_client_request_body_size) = &self.http_client_request_body_size { + http_client_request_body_size.on_response(response); + } + if let Some(http_client_response_body_size) = &self.http_client_response_body_size { + http_client_response_body_size.on_response(response); + } + self.custom.on_response(response); + } + + fn on_error(&self, error: &BoxError, ctx: &Context) { + if let Some(http_client_request_duration) = &self.http_client_request_duration { + http_client_request_duration.on_error(error, ctx); + } + if let Some(http_client_request_body_size) = &self.http_client_request_body_size { + http_client_request_body_size.on_error(error, ctx); + } + if let Some(http_client_response_body_size) = &self.http_client_response_body_size { + http_client_response_body_size.on_error(error, ctx); + } + self.custom.on_error(error, ctx); + } +} + pub(crate) type RouterCustomInstruments = CustomInstruments; @@ -740,6 +883,11 @@ pub(crate) type SubgraphCustomInstruments = CustomInstruments; // ---------------- Counter ----------------------- +enum Increment { + Unit, + Duration(Instant), + Custom(Option), +} struct CustomCounter where @@ -762,12 +910,6 @@ where attributes: Vec, } -enum Increment { - Unit, - Duration(Instant), - Custom(Option), -} - impl Instrumented for CustomCounter where A: Selectors + Default, @@ -862,6 +1004,87 @@ where } } +struct ActiveRequestsCounter { + inner: Mutex, +} + +struct ActiveRequestsCounterInner { + counter: Option>, + attrs_config: ActiveRequestsAttributes, + attributes: Vec, +} + +impl Instrumented for ActiveRequestsCounter { + type Request = router::Request; + type Response = router::Response; + + fn on_request(&self, request: &Self::Request) { + let mut inner = self.inner.lock(); + if inner.attrs_config.http_request_method { + if let Some(attr) = (RouterSelector::RequestMethod { + request_method: true, + }) + .on_request(request) + { + inner + .attributes + .push(KeyValue::new(HTTP_REQUEST_METHOD, attr)); + } + } + if inner.attrs_config.server_address { + if let Some(attr) = HttpServerAttributes::forwarded_host(request) + .and_then(|h| h.host().map(|h| h.to_string())) + { + inner.attributes.push(KeyValue::new(SERVER_ADDRESS, attr)); + } + } + if inner.attrs_config.server_port { + if let Some(attr) = + HttpServerAttributes::forwarded_host(request).and_then(|h| h.port_u16()) + { + inner + .attributes + .push(KeyValue::new(SERVER_PORT, attr as i64)); + } + } + if inner.attrs_config.url_scheme { + if let Some(attr) = request.router_request.uri().scheme_str() { + inner + .attributes + .push(KeyValue::new(URL_SCHEME, attr.to_string())); + } + } + if let Some(counter) = &inner.counter { + counter.add(1, &inner.attributes); + } + } + + fn on_response(&self, _response: &Self::Response) { + let mut inner = self.inner.lock(); + if let Some(counter) = &inner.counter.take() { + counter.add(-1, &inner.attributes); + } + } + + fn on_error(&self, _error: &BoxError, _ctx: &Context) { + let mut inner = self.inner.lock(); + if let Some(counter) = &inner.counter.take() { + counter.add(-1, &inner.attributes); + } + } +} + +impl Drop for ActiveRequestsCounter { + fn drop(&mut self) { + let inner = self.inner.try_lock(); + if let Some(mut inner) = inner { + if let Some(counter) = &inner.counter.take() { + counter.add(-1, &inner.attributes); + } + } + } +} + // ---------------- Histogram ----------------------- struct CustomHistogram @@ -879,7 +1102,7 @@ where { increment: Increment, selector: Option>, - selectors: Extendable, + selectors: Option>, histogram: Option>, attributes: Vec, } @@ -894,7 +1117,9 @@ where fn on_request(&self, request: &Self::Request) { let mut inner = self.inner.lock(); - inner.attributes = inner.selectors.on_request(request).into_iter().collect(); + if let Some(selectors) = &inner.selectors { + inner.attributes = selectors.on_request(request).into_iter().collect(); + } if let Some(selected_value) = inner.selector.as_ref().and_then(|s| s.on_request(request)) { inner.increment = Increment::Custom(selected_value.as_str().parse::().ok()) } @@ -902,7 +1127,11 @@ where fn on_response(&self, response: &Self::Response) { let mut inner = self.inner.lock(); - let mut attrs: Vec = inner.selectors.on_response(response).into_iter().collect(); + let mut attrs: Vec = inner + .selectors + .as_ref() + .map(|s| s.on_response(response).into_iter().collect()) + .unwrap_or_default(); attrs.append(&mut inner.attributes); if let Some(selected_value) = inner @@ -926,7 +1155,11 @@ where fn on_error(&self, error: &BoxError, _ctx: &Context) { let mut inner = self.inner.lock(); - let mut attrs: Vec = inner.selectors.on_error(error).into_iter().collect(); + let mut attrs: Vec = inner + .selectors + .as_ref() + .map(|s| s.on_error(error).into_iter().collect()) + .unwrap_or_default(); attrs.append(&mut inner.attributes); let increment = match inner.increment { diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 87845f977c..66801cf006 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -82,6 +82,11 @@ pub(crate) enum RouterSelector { /// Optional default value. default: Option, }, + /// The request method. + RequestMethod { + /// The request method enabled or not + request_method: bool, + }, /// A header from the response ResponseHeader { /// The name of the request header. @@ -446,6 +451,9 @@ impl Selector for RouterSelector { fn on_request(&self, request: &router::Request) -> Option { match self { + RouterSelector::RequestMethod { request_method } if *request_method => { + Some(request.router_request.method().to_string().into()) + } RouterSelector::RequestHeader { request_header, default, diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index ed67ba6b40..0399c8c321 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -61,8 +61,8 @@ use self::config::Sampler; use self::config::SamplerOption; use self::config::TraceIdFormat; use self::config_new::instruments::Instrumented; -use self::config_new::instruments::RouterCustomInstruments; -use self::config_new::instruments::SubgraphCustomInstruments; +use self::config_new::instruments::RouterInstruments; +use self::config_new::instruments::SubgraphInstruments; use self::config_new::instruments::SupergraphCustomInstruments; use self::config_new::spans::Spans; use self::metrics::apollo::studio::SingleTypeStat; @@ -343,11 +343,6 @@ impl Plugin for Telemetry { .attributes .on_request(request); - config_request - .instrumentation - .instruments - .router - .on_request(request); custom_attributes.extend([ KeyValue::new(CLIENT_NAME_KEY, client_name.to_string()), KeyValue::new(CLIENT_VERSION_KEY, client_version.to_string()), @@ -360,9 +355,10 @@ impl Plugin for Telemetry { ), ]); - let custom_instruments: RouterCustomInstruments = RouterCustomInstruments::new( - &config_request.instrumentation.instruments.router.custom, - ); + let custom_instruments: RouterInstruments = config_request + .instrumentation + .instruments + .new_router_instruments(); custom_instruments.on_request(request); ( @@ -373,7 +369,7 @@ impl Plugin for Telemetry { }, move |(custom_attributes, custom_instruments, ctx): ( LinkedList, - RouterCustomInstruments, + RouterInstruments, Context, ), fut| { @@ -402,11 +398,6 @@ impl Plugin for Telemetry { .attributes .on_response(response), ); - config - .instrumentation - .instruments - .router - .on_response(response); custom_instruments.on_response(response); if expose_trace_id.enabled { @@ -440,11 +431,6 @@ impl Plugin for Telemetry { span.set_dyn_attributes( config.instrumentation.spans.router.attributes.on_error(err), ); - config - .instrumentation - .instruments - .router - .on_error(err, &ctx); custom_instruments.on_error(err, &ctx); } @@ -621,14 +607,10 @@ impl Plugin for Telemetry { .subgraph .attributes .on_request(sub_request); - config + let custom_instruments = config .instrumentation .instruments - .subgraph - .on_request(sub_request); - let custom_instruments = SubgraphCustomInstruments::new( - &config.instrumentation.instruments.subgraph.custom, - ); + .new_subgraph_instruments(); custom_instruments.on_request(sub_request); ( @@ -639,7 +621,7 @@ impl Plugin for Telemetry { }, move |(context, custom_instruments, custom_attributes): ( Context, - SubgraphCustomInstruments, + SubgraphInstruments, LinkedList, ), f: BoxFuture<'static, Result>| { @@ -667,7 +649,6 @@ impl Plugin for Telemetry { .attributes .on_response(resp), ); - conf.instrumentation.instruments.subgraph.on_response(resp); custom_instruments.on_response(resp); } Err(err) => { @@ -676,10 +657,6 @@ impl Plugin for Telemetry { span.set_dyn_attributes( conf.instrumentation.spans.subgraph.attributes.on_error(err), ); - conf.instrumentation - .instruments - .subgraph - .on_error(err, &context); custom_instruments.on_error(err, &context); } } @@ -1870,6 +1847,7 @@ mod tests { use axum::headers::HeaderName; use dashmap::DashMap; + use http::header::CONTENT_TYPE; use http::HeaderMap; use http::HeaderValue; use http::StatusCode; @@ -1893,15 +1871,19 @@ mod tests { use super::apollo::ForwardHeaders; use super::Telemetry; use crate::error::FetchError; + use crate::graphql; use crate::graphql::Error; use crate::graphql::Request; use crate::http_ext; use crate::json_ext::Object; use crate::metrics::FutureMetricsExt; + use crate::plugin::test::MockRouterService; use crate::plugin::test::MockSubgraphService; use crate::plugin::test::MockSupergraphService; use crate::plugin::DynPlugin; use crate::plugins::telemetry::handle_error_internal; + use crate::services::RouterRequest; + use crate::services::RouterResponse; use crate::services::SubgraphRequest; use crate::services::SubgraphResponse; use crate::services::SupergraphRequest; @@ -2023,7 +2005,7 @@ mod tests { "status" = "200", "x-custom" = "coming_from_header" ); - assert_histogram!( + assert_histogram_sum!( "apollo_router_http_request_duration_seconds", 1, "another_test" = "my_default_value", @@ -2084,6 +2066,285 @@ mod tests { .await; } + #[tokio::test] + async fn test_custom_router_instruments() { + async { + let plugin = + create_plugin_with_config(include_str!("testdata/custom_instruments.router.yaml")) + .await; + + let mut mock_bad_request_service = MockRouterService::new(); + mock_bad_request_service + .expect_call() + .times(2) + .returning(move |req: RouterRequest| { + Ok(RouterResponse::fake_builder() + .context(req.context) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap()) + }); + let mut bad_request_router_service = + plugin.router_service(BoxService::new(mock_bad_request_service)); + let router_req = RouterRequest::fake_builder() + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql"); + let _router_response = bad_request_router_service + .ready() + .await + .unwrap() + .call(router_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + assert_counter!("acme.graphql.custom_req", 1.0); + assert_histogram_sum!( + "http.server.request.body.size", + 55.0, + "http.response.status_code" = 400, + "acme.my_attribute" = "application/json" + ); + assert_histogram_sum!("acme.request.length", 55.0); + + let router_req = RouterRequest::fake_builder() + .header("x-custom", "TEST") + .header("custom-length", "5") + .header("content-length", "5") + .header("content-type", "application/graphql"); + let _router_response = bad_request_router_service + .ready() + .await + .unwrap() + .call(router_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + assert_counter!("acme.graphql.custom_req", 1.0); + assert_histogram_sum!("acme.request.length", 60.0); + assert_histogram_sum!( + "http.server.request.body.size", + 60.0, + "http.response.status_code" = 400, + "acme.my_attribute" = "application/json" + ); + } + .with_metrics() + .await; + } + + #[tokio::test] + async fn test_custom_supergraph_instruments() { + async { + let plugin = + create_plugin_with_config(include_str!("testdata/custom_instruments.router.yaml")) + .await; + + let mut mock_bad_request_service = MockSupergraphService::new(); + mock_bad_request_service.expect_call().times(3).returning( + move |req: SupergraphRequest| { + Ok(SupergraphResponse::fake_builder() + .context(req.context) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap()) + }, + ); + let mut bad_request_supergraph_service = + plugin.supergraph_service(BoxService::new(mock_bad_request_service)); + let supergraph_req = SupergraphRequest::fake_builder() + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql") + .query("Query test { me {name} }") + .operation_name("test".to_string()); + let _router_response = bad_request_supergraph_service + .ready() + .await + .unwrap() + .call(supergraph_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + assert_counter!( + "acme.graphql.requests", + 1.0, + "acme.my_attribute" = "application/json", + "graphql_query" = "Query test { me {name} }", + "graphql.document" = "Query test { me {name} }" + ); + + let supergraph_req = SupergraphRequest::fake_builder() + .header("x-custom", "TEST") + .header("custom-length", "5") + .header("content-length", "5") + .header("content-type", "application/graphql") + .query("Query test { me {name} }") + .operation_name("test".to_string()); + + let _router_response = bad_request_supergraph_service + .ready() + .await + .unwrap() + .call(supergraph_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + assert_counter!( + "acme.graphql.requests", + 2.0, + "acme.my_attribute" = "application/json", + "graphql_query" = "Query test { me {name} }", + "graphql.document" = "Query test { me {name} }" + ); + + let supergraph_req = SupergraphRequest::fake_builder() + .header("custom-length", "5") + .header("content-length", "5") + .header("content-type", "application/graphql") + .query("Query test { me {name} }") + .operation_name("test".to_string()); + + let _router_response = bad_request_supergraph_service + .ready() + .await + .unwrap() + .call(supergraph_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + assert_counter!( + "acme.graphql.requests", + 2.0, + "acme.my_attribute" = "application/json", + "graphql_query" = "Query test { me {name} }", + "graphql.document" = "Query test { me {name} }" + ); + } + .with_metrics() + .await; + } + + #[tokio::test] + async fn test_custom_subgraph_instruments() { + async { + let plugin = + create_plugin_with_config(include_str!("testdata/custom_instruments.router.yaml")) + .await; + + let mut mock_bad_request_service = MockSubgraphService::new(); + mock_bad_request_service.expect_call().times(2).returning( + move |req: SubgraphRequest| { + let mut headers = HeaderMap::new(); + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); + let errors = vec![ + graphql::Error::builder() + .message("nope".to_string()) + .extension_code("NOPE") + .build(), + graphql::Error::builder() + .message("nok".to_string()) + .extension_code("NOK") + .build(), + ]; + Ok(SubgraphResponse::fake_builder() + .context(req.context) + .status_code(StatusCode::BAD_REQUEST) + .headers(headers) + .errors(errors) + .build()) + }, + ); + let mut bad_request_subgraph_service = + plugin.subgraph_service("test", BoxService::new(mock_bad_request_service)); + let sub_req = http::Request::builder() + .method("POST") + .uri("http://test") + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql") + .body(graphql::Request::builder().query("{ me {name} }").build()) + .unwrap(); + let subgraph_req = SubgraphRequest::fake_builder() + .subgraph_request(sub_req) + .subgraph_name("test".to_string()) + .build(); + + let _router_response = bad_request_subgraph_service + .ready() + .await + .unwrap() + .call(subgraph_req) + .await + .unwrap(); + + assert_counter!( + "acme.subgraph.error_reqs", + 1.0, + graphql_error = opentelemetry::Value::Array(opentelemetry::Array::String(vec![ + "nope".into(), + "nok".into() + ])), + subgraph.name = "test" + ); + let sub_req = http::Request::builder() + .method("POST") + .uri("http://test") + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql") + .body(graphql::Request::builder().query("{ me {name} }").build()) + .unwrap(); + let subgraph_req = SubgraphRequest::fake_builder() + .subgraph_request(sub_req) + .subgraph_name("test".to_string()) + .build(); + + let _router_response = bad_request_subgraph_service + .ready() + .await + .unwrap() + .call(subgraph_req) + .await + .unwrap(); + assert_counter!( + "acme.subgraph.error_reqs", + 2.0, + graphql_error = opentelemetry::Value::Array(opentelemetry::Array::String(vec![ + "nope".into(), + "nok".into() + ])), + subgraph.name = "test" + ); + } + .with_metrics() + .await; + } + #[tokio::test] async fn test_subgraph_metrics_ok() { async { @@ -2259,7 +2520,7 @@ mod tests { "renamed_value" = "my_value_set", "status" = "400" ); - assert_histogram!( + assert_histogram_sum!( "apollo_router_http_request_duration_seconds", 1, "another_test" = "my_default_value", diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml new file mode 100644 index 0000000000..2cacb6f29c --- /dev/null +++ b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml @@ -0,0 +1,76 @@ +telemetry: + apollo: + client_name_header: name_header + client_version_header: version_header + instrumentation: + instruments: + default_attribute_requirement_level: none + router: + http.server.request.body.size: + attributes: + # Standard attributes + http.response.status_code: true + "acme.my_attribute": + response_header: "content-type" + acme.request.duration: # The name of your custom instrument/metric + value: duration + type: counter + unit: s + description: "my description" + acme.graphql.custom_req: + value: unit + type: counter + unit: request + description: "supergraph requests" + condition: + exist: + request_header: "conditional-custom" + acme.request.size: # The name of your custom instrument/metric + value: + request_header: "custom-length" + type: counter + unit: s + condition: + all: + - eq: + - request_header: "x-custom" + - "TEST" + - eq: + - response_header: "content-type" + - "application/graphql" + description: "my description" + + acme.request.length: # The name of your custom instrument/metric + value: + request_header: "custom-length" + type: histogram + unit: s + description: "my description" + supergraph: + acme.graphql.requests: + value: unit + type: counter + unit: request + description: "supergraph requests" + attributes: + graphql.document: true + graphql_query: + query: string + "acme.my_attribute": + response_header: "content-type" + condition: + exist: + request_header: "x-custom" + subgraph: + acme.subgraph.error_reqs: + value: unit + type: counter + unit: request + description: "subgraph requests in error" + attributes: + subgraph.name: true + graphql_error: + subgraph_response_errors: "$[*].message" + condition: + exist: + subgraph_response_errors: "$[*].message" \ No newline at end of file From 360f1e8a2beb6e939933f9529a699003706c0ab3 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:05:49 +0100 Subject: [PATCH 09/25] fix tests Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 19920 ++++++++++++++-- apollo-router/src/plugins/telemetry/mod.rs | 28 - 2 files changed, 17791 insertions(+), 2157 deletions(-) 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 a553da9f4c..a57aac6573 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 @@ -6523,12 +6523,12 @@ expression: "&schema" "description": "Instrumentation configuration", "type": "object", "properties": { - "spans": { - "description": "Span configuration", + "instruments": { + "description": "Instrument configuration", "type": "object", "properties": { "default_attribute_requirement_level": { - "description": "The attributes to include by default in spans based on their level as specified in the otel semantic conventions and Apollo documentation.", + "description": "The attributes to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation.", "oneOf": [ { "description": "No default attributes set on spans, you have to set it one by one in the configuration to enable some attributes", @@ -6553,2364 +6553,18026 @@ expression: "&schema" } ] }, - "mode": { - "description": "Use new OpenTelemetry spec compliant span attributes or preserve existing. This will be defaulted in future to `spec_compliant`, eventually removed in future.", - "oneOf": [ - { - "description": "Keep the request span as root span and deprecated attributes. This option will eventually removed.", - "type": "string", - "enum": [ - "deprecated" - ] - }, - { - "description": "Use new OpenTelemetry spec compliant span attributes or preserve existing. This will be the default in future.", - "type": "string", - "enum": [ - "spec_compliant" - ] - } - ] - }, "router": { - "description": "Configuration of router spans. Log events inherit attributes from the containing span, so attributes configured here will be included on log events for a request. Router spans contain http request and response information and therefore contain http specific attributes.", + "description": "Router service instruments. For more information see documentation on Router lifecycle.", "type": "object", "properties": { - "attributes": { - "description": "Custom attributes that are attached to the router span.", - "type": "object", - "properties": { - "baggage": { - "description": "All key values from trace baggage.", - "default": null, - "type": "boolean", - "nullable": true - }, - "dd.trace_id": { - "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", - "default": null, - "type": "boolean", - "nullable": true - }, - "error.type": { - "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", - "default": null, - "type": "boolean", - "nullable": true - }, - "http.request.body.size": { - "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "http.request.method": { - "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true - }, - "http.response.body.size": { - "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "http.response.status_code": { - "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", - "default": null, - "type": "boolean", - "nullable": true - }, - "http.route": { - "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.local.address": { - "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.local.port": { - "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.peer.address": { - "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.peer.port": { - "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.protocol.name": { - "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.protocol.version": { - "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.transport": { - "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", - "default": null, - "type": "boolean", - "nullable": true - }, - "network.type": { - "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "server.address": { - "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "server.port": { - "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - }, - "trace_id": { - "description": "The OpenTelemetry trace ID. This can be output in logs.", - "default": null, - "type": "boolean", - "nullable": true - }, - "url.path": { - "description": "The URI path component Examples: * /search Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true - }, - "url.query": { - "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", - "default": null, - "type": "boolean", - "nullable": true - }, - "url.scheme": { - "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true + "http.server.active_requests": { + "description": "Counter of active requests", + "anyOf": [ + { + "type": "boolean" }, - "user_agent.original": { - "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "anyOf": [ - { - "description": "A header from the request", - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "http_request_method": { + "default": false, + "type": "boolean" + }, + "server_address": { + "default": false, + "type": "boolean" + }, + "server_port": { + "default": false, + "type": "boolean" + }, + "url_scheme": { + "default": false, + "type": "boolean" + } }, - "request_header": { - "description": "The name of the request header.", + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "http.server.request.body.size": { + "description": "Histogram of server request body size", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "description": "Common attributes for http server and client. See https://opentelemetry.io/docs/specs/semconv/http/http-spans/#common-attributes", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + }, + "http.server.request.duration": { + "description": "Histogram of server request duration", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "description": "Common attributes for http server and client. See https://opentelemetry.io/docs/specs/semconv/http/http-spans/#common-attributes", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + }, + "http.server.response.body.size": { + "description": "Histogram of server response body size", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "description": "Common attributes for http server and client. See https://opentelemetry.io/docs/specs/semconv/http/http-spans/#common-attributes", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + } + }, + "additionalProperties": { + "type": "object", + "required": [ + "description", + "type", + "unit", + "value" + ], + "properties": { + "attributes": { + "description": "Attributes to include on the instrument.", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The instrument conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exist" + ], + "properties": { + "exist": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "description": { + "description": "The description of the instrument.", + "type": "string" + }, + "type": { + "description": "The type of instrument.", + "oneOf": [ + { + "description": "A monotonic counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums", + "type": "string", + "enum": [ + "counter" + ] + }, + { + "description": "A histogram https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram", + "type": "string", + "enum": [ + "histogram" + ] + } + ] + }, + "unit": { + "description": "The units of the instrument, e.g. \"ms\", \"bytes\", \"requests\".", + "type": "string" + }, + "value": { + "description": "The value of the instrument.", + "anyOf": [ + { + "type": "string", + "enum": [ + "duration", + "unit" + ] + }, + { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + } + } + } + }, + "subgraph": { + "description": "Subgraph service instruments. For more information see documentation on Router lifecycle.", + "type": "object", + "properties": { + "http.client.request.body.size": { + "description": "Histogram of client request body size", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + }, + "http.client.request.duration": { + "description": "Histogram of client request duration", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + }, + "http.client.response.body.size": { + "description": "Histogram of client response body size", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + ] + } + }, + "additionalProperties": { + "type": "object", + "required": [ + "description", + "type", + "unit", + "value" + ], + "properties": { + "attributes": { + "description": "Attributes to include on the instrument.", + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The instrument conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exist" + ], + "properties": { + "exist": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "description": { + "description": "The description of the instrument.", + "type": "string" + }, + "type": { + "description": "The type of instrument.", + "oneOf": [ + { + "description": "A monotonic counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums", + "type": "string", + "enum": [ + "counter" + ] + }, + { + "description": "A histogram https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram", + "type": "string", + "enum": [ + "histogram" + ] + } + ] + }, + "unit": { + "description": "The units of the instrument, e.g. \"ms\", \"bytes\", \"requests\".", + "type": "string" + }, + "value": { + "description": "The value of the instrument.", + "anyOf": [ + { + "type": "string", + "enum": [ + "duration", + "unit" + ] + }, + { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + } + } + } + }, + "supergraph": { + "description": "Supergraph service instruments. For more information see documentation on Router lifecycle.", + "type": "object", + "additionalProperties": { + "type": "object", + "required": [ + "description", + "type", + "unit", + "value" + ], + "properties": { + "attributes": { + "description": "Attributes to include on the instrument.", + "type": "object", + "properties": { + "graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "condition": { + "description": "The instrument conditions.", + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exist" + ], + "properties": { + "exist": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "description": { + "description": "The description of the instrument.", + "type": "string" + }, + "type": { + "description": "The type of instrument.", + "oneOf": [ + { + "description": "A monotonic counter https://opentelemetry.io/docs/specs/otel/metrics/data-model/#sums", + "type": "string", + "enum": [ + "counter" + ] + }, + { + "description": "A histogram https://opentelemetry.io/docs/specs/otel/metrics/data-model/#histogram", + "type": "string", + "enum": [ + "histogram" + ] + } + ] + }, + "unit": { + "description": "The units of the instrument, e.g. \"ms\", \"bytes\", \"requests\".", + "type": "string" + }, + "value": { + "description": "The value of the instrument.", + "anyOf": [ + { + "type": "string", + "enum": [ + "duration", + "unit" + ] + }, + { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + } + } + } + } + }, + "additionalProperties": false + }, + "spans": { + "description": "Span configuration", + "type": "object", + "properties": { + "default_attribute_requirement_level": { + "description": "The attributes to include by default in spans based on their level as specified in the otel semantic conventions and Apollo documentation.", + "oneOf": [ + { + "description": "No default attributes set on spans, you have to set it one by one in the configuration to enable some attributes", + "type": "string", + "enum": [ + "none" + ] + }, + { + "description": "Attributes that are marked as required in otel semantic conventions and apollo documentation will be included (default)", + "type": "string", + "enum": [ + "required" + ] + }, + { + "description": "Attributes that are marked as required or recommended in otel semantic conventions and apollo documentation will be included", + "type": "string", + "enum": [ + "recommended" + ] + } + ] + }, + "mode": { + "description": "Use new OpenTelemetry spec compliant span attributes or preserve existing. This will be defaulted in future to `spec_compliant`, eventually removed in future.", + "oneOf": [ + { + "description": "Keep the request span as root span and deprecated attributes. This option will eventually removed.", + "type": "string", + "enum": [ + "deprecated" + ] + }, + { + "description": "Use new OpenTelemetry spec compliant span attributes or preserve existing. This will be the default in future.", + "type": "string", + "enum": [ + "spec_compliant" + ] + } + ] + }, + "router": { + "description": "Configuration of router spans. Log events inherit attributes from the containing span, so attributes configured here will be included on log events for a request. Router spans contain http request and response information and therefore contain http specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the router span.", + "type": "object", + "properties": { + "baggage": { + "description": "All key values from trace baggage.", + "default": null, + "type": "boolean", + "nullable": true + }, + "dd.trace_id": { + "description": "The datadog trace ID. This can be output in logs and used to correlate traces in Datadog.", + "default": null, + "type": "boolean", + "nullable": true + }, + "error.type": { + "description": "Describes a class of error the operation ended with. Examples: * timeout * name_resolution_error * 500 Requirement level: Conditionally Required: If request has ended with an error.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.body.size": { + "description": "The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.request.method": { + "description": "HTTP request method. Examples: * GET * POST * HEAD Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.body.size": { + "description": "The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the Content-Length header. For requests using transport encoding, this should be the compressed size. Examples: * 3495 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.response.status_code": { + "description": "HTTP response status code. Examples: * 200 Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "http.route": { + "description": "The matched route (path template in the format used by the respective server framework). Examples: * /graphql Requirement level: Conditionally Required: If and only if it’s available", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.address": { + "description": "Local socket address. Useful in case of a multi-IP host. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.local.port": { + "description": "Local socket port. Useful in case of a multi-port host. Examples: * 65123 Requirement level: Opt-In", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.address": { + "description": "Peer address of the network connection - IP address or Unix domain socket name. Examples: * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.peer.port": { + "description": "Peer port number of the network connection. Examples: * 65123 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.name": { + "description": "OSI application layer or non-OSI equivalent. Examples: * http * spdy Requirement level: Recommended: if not default (http).", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.protocol.version": { + "description": "Version of the protocol specified in network.protocol.name. Examples: * 1.0 * 1.1 * 2 * 3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.transport": { + "description": "OSI transport layer. Examples: * tcp * udp Requirement level: Conditionally Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "network.type": { + "description": "OSI network layer or non-OSI equivalent. Examples: * ipv4 * ipv6 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.address": { + "description": "Name of the local HTTP server that received the request. Examples: * example.com * 10.1.2.80 * /tmp/my.sock Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "server.port": { + "description": "Port of the local HTTP server that received the request. Examples: * 80 * 8080 * 443 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "trace_id": { + "description": "The OpenTelemetry trace ID. This can be output in logs.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.path": { + "description": "The URI path component Examples: * /search Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.query": { + "description": "The URI query component Examples: * q=OpenTelemetry Requirement level: Conditionally Required: If and only if one was received/sent.", + "default": null, + "type": "boolean", + "nullable": true + }, + "url.scheme": { + "description": "The URI scheme component identifying the used protocol. Examples: * http * https Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + }, + "user_agent.original": { + "description": "Value of the HTTP User-Agent header sent by the client. Examples: * CERN-LineMode/2.15 * libwww/2.17b3 Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + }, + "subgraph": { + "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the subgraph span.", + "type": "object", + "properties": { + "subgraph.graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "subgraph.name": { + "description": "The name of the subgraph Examples: * products Requirement level: Required", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + }, + "supergraph": { + "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", + "type": "object", + "properties": { + "attributes": { + "description": "Custom attributes that are attached to the supergraph span.", + "default": { + "attributes": { + "graphql.document": null, + "graphql.operation.name": null, + "graphql.operation.type": null + }, + "custom": {} + }, + "type": "object", + "properties": { + "graphql.document": { + "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.name": { + "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + }, + "graphql.operation.type": { + "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", + "default": null, + "type": "boolean", + "nullable": true + } + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "tls": { + "description": "TLS related configuration options.", + "default": { + "supergraph": null, + "subgraph": { + "all": { + "certificate_authorities": null, + "client_authentication": null + }, + "subgraphs": {} + } + }, + "type": "object", + "properties": { + "subgraph": { + "description": "Configuration options pertaining to the subgraph server component.", + "default": { + "all": { + "certificate_authorities": null, + "client_authentication": null + }, + "subgraphs": {} + }, + "type": "object", + "properties": { + "all": { + "description": "options applying to all subgraphs", + "default": { + "certificate_authorities": null, + "client_authentication": null + }, + "type": "object", + "properties": { + "certificate_authorities": { + "description": "list of certificate authorities in PEM format", + "default": null, + "type": "string", + "nullable": true + }, + "client_authentication": { + "description": "client certificate authentication", + "default": null, + "type": "object", + "required": [ + "certificate_chain", + "key" + ], + "properties": { + "certificate_chain": { + "description": "list of certificates in PEM format", + "writeOnly": true, + "type": "string" + }, + "key": { + "description": "key in PEM format", + "writeOnly": true, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + } + }, + "additionalProperties": false + }, + "subgraphs": { + "description": "per subgraph options", + "default": {}, + "type": "object", + "additionalProperties": { + "description": "Configuration options pertaining to the subgraph server component.", + "type": "object", + "properties": { + "certificate_authorities": { + "description": "list of certificate authorities in PEM format", + "default": null, + "type": "string", + "nullable": true + }, + "client_authentication": { + "description": "client certificate authentication", + "default": null, + "type": "object", + "required": [ + "certificate_chain", + "key" + ], + "properties": { + "certificate_chain": { + "description": "list of certificates in PEM format", + "writeOnly": true, + "type": "string" + }, + "key": { + "description": "key in PEM format", + "writeOnly": true, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + } + }, + "additionalProperties": false + } + } + } + }, + "supergraph": { + "description": "TLS server configuration\n\nthis will affect the GraphQL endpoint and any other endpoint targeting the same listen address", + "default": null, + "type": "object", + "required": [ + "certificate", + "certificate_chain", + "key" + ], + "properties": { + "certificate": { + "description": "server certificate in PEM format", + "writeOnly": true, + "type": "string" + }, + "certificate_chain": { + "description": "list of certificate authorities in PEM format", + "writeOnly": true, + "type": "string" + }, + "key": { + "description": "server key in PEM format", + "writeOnly": true, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + } + }, + "additionalProperties": false + }, + "traffic_shaping": { + "description": "Configuration for the experimental traffic shaping plugin", + "type": "object", + "properties": { + "all": { + "description": "Applied on all subgraphs", + "type": "object", + "properties": { + "compression": { + "description": "Enable compression for subgraphs (available compressions are deflate, br, gzip)", + "oneOf": [ + { + "description": "gzip", + "type": "string", + "enum": [ + "gzip" + ] + }, + { + "description": "deflate", + "type": "string", + "enum": [ + "deflate" + ] + }, + { + "description": "brotli", + "type": "string", + "enum": [ + "br" + ] + }, + { + "description": "identity", + "type": "string", + "enum": [ + "identity" + ] + } + ], + "nullable": true + }, + "deduplicate_query": { + "description": "Enable query deduplication", + "type": "boolean", + "nullable": true + }, + "experimental_http2": { + "description": "Enable HTTP2 for subgraphs", + "oneOf": [ + { + "description": "Enable HTTP2 for subgraphs", + "type": "string", + "enum": [ + "enable" + ] + }, + { + "description": "Disable HTTP2 for subgraphs", + "type": "string", + "enum": [ + "disable" + ] + }, + { + "description": "Only HTTP2 is active", + "type": "string", + "enum": [ + "http2only" + ] + } + ], + "nullable": true + }, + "experimental_retry": { + "description": "Retry configuration", + "type": "object", + "properties": { + "min_per_sec": { + "description": "minimum rate of retries allowed to accomodate clients that have just started issuing requests, or clients that do not issue many requests per window. The default value is 10", + "type": "integer", + "format": "uint32", + "minimum": 0.0, + "nullable": true + }, + "retry_mutations": { + "description": "allows request retries on mutations. This should only be activated if mutations are idempotent. Disabled by default", + "type": "boolean", + "nullable": true + }, + "retry_percent": { + "description": "percentage of calls to deposit that can be retried. This is in addition to any retries allowed for via min_per_sec. Must be between 0 and 1000, default value is 0.2", + "type": "number", + "format": "float", + "nullable": true + }, + "ttl": { + "description": "how long a single deposit should be considered. Must be between 1 and 60 seconds, default value is 10 seconds", + "default": null, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "global_rate_limit": { + "description": "Enable global rate limiting", + "type": "object", + "required": [ + "capacity", + "interval" + ], + "properties": { + "capacity": { + "description": "Number of requests allowed", + "type": "integer", + "format": "uint64", + "minimum": 1.0 + }, + "interval": { + "description": "Per interval", + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "timeout": { + "description": "Enable timeout for incoming requests", + "default": null, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "deduplicate_variables": { + "description": "DEPRECATED, now always enabled: Enable variable deduplication optimization when sending requests to subgraphs (https://github.com/apollographql/router/issues/87)", + "default": null, + "type": "boolean", + "nullable": true + }, + "router": { + "description": "Applied at the router level", + "type": "object", + "properties": { + "global_rate_limit": { + "description": "Enable global rate limiting", + "type": "object", + "required": [ + "capacity", + "interval" + ], + "properties": { + "capacity": { + "description": "Number of requests allowed", + "type": "integer", + "format": "uint64", + "minimum": 1.0 + }, + "interval": { + "description": "Per interval", + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "timeout": { + "description": "Enable timeout for incoming requests", + "default": null, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "subgraphs": { + "description": "Applied on specific subgraphs", + "type": "object", + "additionalProperties": { + "description": "Traffic shaping options", + "type": "object", + "properties": { + "compression": { + "description": "Enable compression for subgraphs (available compressions are deflate, br, gzip)", + "oneOf": [ + { + "description": "gzip", + "type": "string", + "enum": [ + "gzip" + ] + }, + { + "description": "deflate", + "type": "string", + "enum": [ + "deflate" + ] + }, + { + "description": "brotli", + "type": "string", + "enum": [ + "br" + ] + }, + { + "description": "identity", + "type": "string", + "enum": [ + "identity" + ] + } + ], + "nullable": true + }, + "deduplicate_query": { + "description": "Enable query deduplication", + "type": "boolean", + "nullable": true + }, + "experimental_http2": { + "description": "Enable HTTP2 for subgraphs", + "oneOf": [ + { + "description": "Enable HTTP2 for subgraphs", + "type": "string", + "enum": [ + "enable" + ] + }, + { + "description": "Disable HTTP2 for subgraphs", + "type": "string", + "enum": [ + "disable" + ] + }, + { + "description": "Only HTTP2 is active", + "type": "string", + "enum": [ + "http2only" + ] + } + ], + "nullable": true + }, + "experimental_retry": { + "description": "Retry configuration", + "type": "object", + "properties": { + "min_per_sec": { + "description": "minimum rate of retries allowed to accomodate clients that have just started issuing requests, or clients that do not issue many requests per window. The default value is 10", + "type": "integer", + "format": "uint32", + "minimum": 0.0, + "nullable": true + }, + "retry_mutations": { + "description": "allows request retries on mutations. This should only be activated if mutations are idempotent. Disabled by default", + "type": "boolean", + "nullable": true + }, + "retry_percent": { + "description": "percentage of calls to deposit that can be retried. This is in addition to any retries allowed for via min_per_sec. Must be between 0 and 1000, default value is 0.2", + "type": "number", + "format": "float", + "nullable": true + }, + "ttl": { + "description": "how long a single deposit should be considered. Must be between 1 and 60 seconds, default value is 10 seconds", + "default": null, + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "global_rate_limit": { + "description": "Enable global rate limiting", + "type": "object", + "required": [ + "capacity", + "interval" + ], + "properties": { + "capacity": { + "description": "Number of requests allowed", + "type": "integer", + "format": "uint64", + "minimum": 1.0 + }, + "interval": { + "description": "Per interval", + "type": "string" + } + }, + "additionalProperties": false, + "nullable": true + }, + "timeout": { + "description": "Enable timeout for incoming requests", + "default": null, + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "definitions": { + "Condition_for_RouterSelector": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exist" + ], + "properties": { + "exist": { + "anyOf": [ + { + "description": "A header from the request", + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "The request method.", + "type": "object", + "required": [ + "request_method" + ], + "properties": { + "request_method": { + "description": "The request method enabled or not", + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A header from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "The trace ID of the request.", + "type": "object", + "required": [ + "trace_id" + ], + "properties": { + "trace_id": { + "description": "The format of the trace ID.", + "oneOf": [ + { + "description": "Open Telemetry trace ID, a hex string.", + "type": "string", + "enum": [ + "open_telemetry" + ] + }, + { + "description": "Datadog trace ID, a u64.", + "type": "string", + "enum": [ + "datadog" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "A value from context.", + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A value from baggage.", + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "description": "A value from an environment variable.", + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_RouterSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "Condition_for_SubgraphSelector": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "String values", - "type": "string" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_header": { - "description": "The name of the request header.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "A header from the response", - "type": "object", - "required": [ - "response_status" - ], - "properties": { - "response_status": { - "description": "The http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } ] } - }, - "additionalProperties": false - }, - { - "description": "The trace ID of the request.", - "type": "object", - "required": [ - "trace_id" ], - "properties": { - "trace_id": { - "description": "The format of the trace ID.", - "oneOf": [ - { - "description": "Open Telemetry trace ID, a hex string.", - "type": "string", - "enum": [ - "open_telemetry" - ] - }, - { - "description": "Datadog trace ID, a u64.", - "type": "string", - "enum": [ - "datadog" - ] - } - ] - } - }, - "additionalProperties": false + "nullable": true }, - { - "description": "A value from context.", - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" + ] } - }, - "additionalProperties": false - }, - { - "description": "A value from baggage.", - "type": "object", - "required": [ - "baggage" ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" }, - "default": { - "description": "Optional default value.", + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true + ] } - }, - "additionalProperties": false - }, - { - "description": "A value from an environment variable.", - "type": "object", - "required": [ - "env" ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "env": { - "description": "The name of the environment variable", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - }, - "additionalProperties": false + ], + "nullable": true }, - { + "subgraph_response_errors": { + "description": "The subgraph response body json path.", "type": "string" } - ] - } - } - }, - "additionalProperties": false - }, - "subgraph": { - "description": "Attributes to include on the subgraph span. Subgraph spans contain information about the subgraph request and response and therefore contain subgraph specific attributes.", - "type": "object", - "properties": { - "attributes": { - "description": "Custom attributes that are attached to the subgraph span.", - "type": "object", - "properties": { - "subgraph.graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true }, - "subgraph.graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } }, - "subgraph.graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } }, - "subgraph.name": { - "description": "The name of the subgraph Examples: * products Requirement level: Required", - "default": null, - "type": "boolean", - "nullable": true - } + "additionalProperties": false }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "subgraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", "type": "string", - "nullable": true + "enum": [ + "code" + ] }, - "subgraph_operation_name": { - "description": "The operation name from the subgraph query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" ] } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_operation_kind" - ], - "properties": { - "subgraph_operation_kind": { - "description": "The kind of the subgraph operation (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" ] } - }, - "additionalProperties": false + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "type": "object", - "required": [ - "subgraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", "type": "string", - "nullable": true + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "subgraph_query": { - "description": "The graphql query to the subgraph.", - "oneOf": [ + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } ] } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_query_variable" ], - "properties": { - "default": { - "description": "Optional default value.", + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "String values", - "type": "string" + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true - }, - "subgraph_query_variable": { - "description": "The name of a subgraph query variable.", - "type": "string" + ] } - }, - "additionalProperties": false - }, - { - "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", - "type": "object", - "required": [ - "subgraph_response_body" ], - "properties": { - "default": { - "description": "Optional default value.", + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true - }, - "subgraph_response_body": { - "description": "The subgraph response body json path.", + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + ] + }, + "maxItems": 2, + "minItems": 2 + } + }, + "additionalProperties": false + }, + { + "description": "A condition to check a selection against a selector.", + "type": "object", + "required": [ + "exist" + ], + "properties": { + "exist": { + "anyOf": [ + { + "type": "object", + "required": [ + "subgraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_operation_name": { + "description": "The operation name from the subgraph query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_operation_kind" + ], + "properties": { + "subgraph_operation_kind": { + "description": "The kind of the subgraph operation (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_query": { + "description": "The graphql query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } + } + ] + } + ], + "nullable": true + }, + "subgraph_query_variable": { + "description": "The name of a subgraph query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "Deprecated, use SubgraphResponseData and SubgraphResponseError instead", + "type": "object", + "required": [ + "subgraph_response_body" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_data" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_data": { - "description": "The subgraph response body json path.", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_errors" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "subgraph_response_errors": { - "description": "The subgraph response body json path.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_request_header": { - "description": "The name of a subgraph request header.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_body": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_data" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "subgraph_response_header": { - "description": "The name of a subgraph response header.", + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_data": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_errors" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "subgraph_response_status" - ], - "properties": { - "subgraph_response_status": { - "description": "The subgraph http response status code.", - "oneOf": [ - { - "description": "The http status code.", - "type": "string", - "enum": [ - "code" - ] - }, - { - "description": "The http status reason.", - "type": "string", - "enum": [ - "reason" - ] - } - ] + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_operation_name": { - "description": "The supergraph query operation name.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_operation_kind" - ], - "properties": { - "supergraph_operation_kind": { - "description": "The supergraph query operation kind (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "subgraph_response_errors": { + "description": "The subgraph response body json path.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_request_header": { + "description": "The name of a subgraph request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "subgraph_response_header": { + "description": "The name of a subgraph response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "subgraph_response_status" + ], + "properties": { + "subgraph_response_status": { + "description": "The subgraph http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_operation_name": { + "description": "The supergraph query operation name.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_operation_kind" + ], + "properties": { + "supergraph_operation_kind": { + "description": "The supergraph query operation kind (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_query": { + "description": "The supergraph query to the subgraph.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_query": { - "description": "The supergraph query to the subgraph.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "supergraph_query_variable": { - "description": "The supergraph query variable name.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "supergraph_request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "supergraph_request_header": { - "description": "The supergraph request header name.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "request_context": { - "description": "The request context key.", - "type": "string" + } + ] + } + ], + "nullable": true + }, + "supergraph_query_variable": { + "description": "The supergraph query variable name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "supergraph_request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "supergraph_request_header": { + "description": "The supergraph request header name.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" - ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" - }, - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "env": { - "description": "The name of the environment variable", - "type": "string" + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "string" - } - ] - } + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" } }, "additionalProperties": false }, - "supergraph": { - "description": "Configuration of supergraph spans. Supergraph spans contain information about the graphql request and response and therefore contain graphql specific attributes.", + { "type": "object", + "required": [ + "baggage" + ], "properties": { - "attributes": { - "description": "Custom attributes that are attached to the supergraph span.", - "default": { - "attributes": { - "graphql.document": null, - "graphql.operation.name": null, - "graphql.operation.type": null - }, - "custom": {} - }, - "type": "object", - "properties": { - "graphql.document": { - "description": "The GraphQL document being executed. Examples: * query findBookById { bookById(id: ?) { name } } Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "graphql.operation.name": { - "description": "The name of the operation being executed. Examples: * findBookById Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true + { + "description": "i64 values", + "type": "integer", + "format": "int64" }, - "graphql.operation.type": { - "description": "The type of the operation being executed. Examples: * query * subscription * mutation Requirement level: Recommended", - "default": null, - "type": "boolean", - "nullable": true - } - }, - "additionalProperties": { - "anyOf": [ - { - "type": "object", - "required": [ - "operation_name" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "operation_name": { - "description": "The operation name from the query.", - "oneOf": [ - { - "description": "The raw operation name.", - "type": "string", - "enum": [ - "string" - ] - }, - { - "description": "A hash of the operation name.", - "type": "string", - "enum": [ - "hash" - ] - } - ] + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "operation_kind" - ], - "properties": { - "operation_kind": { - "description": "The operation kind from the query (query|mutation|subscription).", - "oneOf": [ - { - "description": "The raw operation kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query" - ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true - }, - "query": { - "description": "The graphql query.", - "oneOf": [ - { - "description": "The raw query kind.", - "type": "string", - "enum": [ - "string" - ] - } - ] + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" } }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "query_variable" - ], - "properties": { - "default": { - "description": "Optional default value.", - "anyOf": [ - { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" - }, - { - "description": "f64 values", - "type": "number", - "format": "double" - }, - { - "description": "String values", - "type": "string" - }, - { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] - } - ], - "nullable": true - }, - "query_variable": { - "description": "The name of a graphql query variable.", + { + "description": "Array of strings", + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + { + "description": "All sub-conditions must be true.", + "type": "object", + "required": [ + "all" + ], + "properties": { + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "At least one sub-conditions must be true.", + "type": "object", + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SubgraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] + } + ] + }, + "Condition_for_SupergraphSelector": { + "oneOf": [ + { + "description": "A condition to check a selection against a value.", + "type": "object", + "required": [ + "eq" + ], + "properties": { + "eq": { + "type": "array", + "items": { + "anyOf": [ + { + "description": "A constant value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "type": "object", - "required": [ - "request_header" - ], - "properties": { - "default": { - "description": "Optional default value.", + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ] + }, + { + "description": "Selector to extract a value from the pipeline.", + "anyOf": [ + { + "type": "object", + "required": [ + "operation_name" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", "type": "string", - "nullable": true + "enum": [ + "string" + ] }, - "request_header": { - "description": "The name of the request header.", - "type": "string" + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] } - }, - "additionalProperties": false + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { - "type": "object", - "required": [ - "response_header" - ], - "properties": { - "default": { - "description": "Optional default value.", + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", "type": "string", - "nullable": true + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "response_header": { - "description": "The name of the response header.", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "request_context" - ], - "properties": { - "default": { - "description": "Optional default value.", + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "request_context": { - "description": "The request context key.", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "response_context" - ], - "properties": { - "default": { - "description": "Optional default value.", + }, + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true - }, - "response_context": { - "description": "The response context key.", - "type": "string" + ] } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "baggage" ], - "properties": { - "baggage": { - "description": "The name of the baggage item.", + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" }, - "default": { - "description": "Optional default value.", + { + "description": "Array of homogeneous values", "anyOf": [ { - "description": "bool values", - "type": "boolean" - }, - { - "description": "i64 values", - "type": "integer", - "format": "int64" + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } }, { - "description": "f64 values", - "type": "number", - "format": "double" + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } }, { - "description": "String values", - "type": "string" + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } }, { - "description": "Array of homogeneous values", - "anyOf": [ - { - "description": "Array of bools", - "type": "array", - "items": { - "type": "boolean" - } - }, - { - "description": "Array of integers", - "type": "array", - "items": { - "type": "integer", - "format": "int64" - } - }, - { - "description": "Array of floats", - "type": "array", - "items": { - "type": "number", - "format": "double" - } - }, - { - "description": "Array of strings", - "type": "array", - "items": { - "type": "string" - } - } - ] + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } } - ], - "nullable": true + ] } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "env" ], - "properties": { - "default": { - "description": "Optional default value.", - "type": "string", - "nullable": true + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" }, - "env": { - "description": "The name of the environment variable", + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] } - }, - "additionalProperties": false + ], + "nullable": true + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - { + "env": { + "description": "The name of the environment variable", "type": "string" } - ] + }, + "additionalProperties": false + }, + { + "type": "string" } - } - }, - "additionalProperties": false - } + ] + } + ] }, - "additionalProperties": false + "maxItems": 2, + "minItems": 2 } }, "additionalProperties": false - } - }, - "additionalProperties": false - }, - "tls": { - "description": "TLS related configuration options.", - "default": { - "supergraph": null, - "subgraph": { - "all": { - "certificate_authorities": null, - "client_authentication": null - }, - "subgraphs": {} - } - }, - "type": "object", - "properties": { - "subgraph": { - "description": "Configuration options pertaining to the subgraph server component.", - "default": { - "all": { - "certificate_authorities": null, - "client_authentication": null - }, - "subgraphs": {} - }, + }, + { + "description": "A condition to check a selection against a selector.", "type": "object", + "required": [ + "exist" + ], "properties": { - "all": { - "description": "options applying to all subgraphs", - "default": { - "certificate_authorities": null, - "client_authentication": null - }, - "type": "object", - "properties": { - "certificate_authorities": { - "description": "list of certificate authorities in PEM format", - "default": null, - "type": "string", - "nullable": true - }, - "client_authentication": { - "description": "client certificate authentication", - "default": null, + "exist": { + "anyOf": [ + { "type": "object", "required": [ - "certificate_chain", - "key" + "operation_name" ], "properties": { - "certificate_chain": { - "description": "list of certificates in PEM format", - "writeOnly": true, - "type": "string" + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - "key": { - "description": "key in PEM format", - "writeOnly": true, - "type": "string" + "operation_name": { + "description": "The operation name from the query.", + "oneOf": [ + { + "description": "The raw operation name.", + "type": "string", + "enum": [ + "string" + ] + }, + { + "description": "A hash of the operation name.", + "type": "string", + "enum": [ + "hash" + ] + } + ] } }, - "additionalProperties": false, - "nullable": true - } - }, - "additionalProperties": false - }, - "subgraphs": { - "description": "per subgraph options", - "default": {}, - "type": "object", - "additionalProperties": { - "description": "Configuration options pertaining to the subgraph server component.", - "type": "object", - "properties": { - "certificate_authorities": { - "description": "list of certificate authorities in PEM format", - "default": null, - "type": "string", - "nullable": true + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "operation_kind" + ], + "properties": { + "operation_kind": { + "description": "The operation kind from the query (query|mutation|subscription).", + "oneOf": [ + { + "description": "The raw operation kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } }, - "client_authentication": { - "description": "client certificate authentication", - "default": null, - "type": "object", - "required": [ - "certificate_chain", - "key" - ], - "properties": { - "certificate_chain": { - "description": "list of certificates in PEM format", - "writeOnly": true, - "type": "string" - }, - "key": { - "description": "key in PEM format", - "writeOnly": true, - "type": "string" - } + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "query" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true }, - "additionalProperties": false, - "nullable": true - } + "query": { + "description": "The graphql query.", + "oneOf": [ + { + "description": "The raw query kind.", + "type": "string", + "enum": [ + "string" + ] + } + ] + } + }, + "additionalProperties": false }, - "additionalProperties": false - } - } - } - }, - "supergraph": { - "description": "TLS server configuration\n\nthis will affect the GraphQL endpoint and any other endpoint targeting the same listen address", - "default": null, - "type": "object", - "required": [ - "certificate", - "certificate_chain", - "key" - ], - "properties": { - "certificate": { - "description": "server certificate in PEM format", - "writeOnly": true, - "type": "string" - }, - "certificate_chain": { - "description": "list of certificate authorities in PEM format", - "writeOnly": true, - "type": "string" - }, - "key": { - "description": "server key in PEM format", - "writeOnly": true, - "type": "string" - } - }, - "additionalProperties": false, - "nullable": true - } - }, - "additionalProperties": false - }, - "traffic_shaping": { - "description": "Configuration for the experimental traffic shaping plugin", - "type": "object", - "properties": { - "all": { - "description": "Applied on all subgraphs", - "type": "object", - "properties": { - "compression": { - "description": "Enable compression for subgraphs (available compressions are deflate, br, gzip)", - "oneOf": [ { - "description": "gzip", - "type": "string", - "enum": [ - "gzip" - ] + "type": "object", + "required": [ + "query_variable" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "query_variable": { + "description": "The name of a graphql query variable.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "deflate", - "type": "string", - "enum": [ - "deflate" - ] + "type": "object", + "required": [ + "request_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "request_header": { + "description": "The name of the request header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "brotli", - "type": "string", - "enum": [ - "br" - ] + "type": "object", + "required": [ + "response_header" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "response_header": { + "description": "The name of the response header.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "identity", - "type": "string", - "enum": [ - "identity" - ] - } - ], - "nullable": true - }, - "deduplicate_query": { - "description": "Enable query deduplication", - "type": "boolean", - "nullable": true - }, - "experimental_http2": { - "description": "Enable HTTP2 for subgraphs", - "oneOf": [ - { - "description": "Enable HTTP2 for subgraphs", - "type": "string", - "enum": [ - "enable" - ] + "type": "object", + "required": [ + "request_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "request_context": { + "description": "The request context key.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Disable HTTP2 for subgraphs", - "type": "string", - "enum": [ - "disable" - ] + "type": "object", + "required": [ + "response_context" + ], + "properties": { + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + }, + "response_context": { + "description": "The response context key.", + "type": "string" + } + }, + "additionalProperties": false }, { - "description": "Only HTTP2 is active", - "type": "string", - "enum": [ - "http2only" - ] - } - ], - "nullable": true - }, - "experimental_retry": { - "description": "Retry configuration", - "type": "object", - "properties": { - "min_per_sec": { - "description": "minimum rate of retries allowed to accomodate clients that have just started issuing requests, or clients that do not issue many requests per window. The default value is 10", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "retry_mutations": { - "description": "allows request retries on mutations. This should only be activated if mutations are idempotent. Disabled by default", - "type": "boolean", - "nullable": true - }, - "retry_percent": { - "description": "percentage of calls to deposit that can be retried. This is in addition to any retries allowed for via min_per_sec. Must be between 0 and 1000, default value is 0.2", - "type": "number", - "format": "float", - "nullable": true + "type": "object", + "required": [ + "baggage" + ], + "properties": { + "baggage": { + "description": "The name of the baggage item.", + "type": "string" + }, + "default": { + "description": "Optional default value.", + "anyOf": [ + { + "description": "bool values", + "type": "boolean" + }, + { + "description": "i64 values", + "type": "integer", + "format": "int64" + }, + { + "description": "f64 values", + "type": "number", + "format": "double" + }, + { + "description": "String values", + "type": "string" + }, + { + "description": "Array of homogeneous values", + "anyOf": [ + { + "description": "Array of bools", + "type": "array", + "items": { + "type": "boolean" + } + }, + { + "description": "Array of integers", + "type": "array", + "items": { + "type": "integer", + "format": "int64" + } + }, + { + "description": "Array of floats", + "type": "array", + "items": { + "type": "number", + "format": "double" + } + }, + { + "description": "Array of strings", + "type": "array", + "items": { + "type": "string" + } + } + ] + } + ], + "nullable": true + } + }, + "additionalProperties": false }, - "ttl": { - "description": "how long a single deposit should be considered. Must be between 1 and 60 seconds, default value is 10 seconds", - "default": null, - "type": "string" - } - }, - "additionalProperties": false, - "nullable": true - }, - "global_rate_limit": { - "description": "Enable global rate limiting", - "type": "object", - "required": [ - "capacity", - "interval" - ], - "properties": { - "capacity": { - "description": "Number of requests allowed", - "type": "integer", - "format": "uint64", - "minimum": 1.0 + { + "type": "object", + "required": [ + "env" + ], + "properties": { + "default": { + "description": "Optional default value.", + "type": "string", + "nullable": true + }, + "env": { + "description": "The name of the environment variable", + "type": "string" + } + }, + "additionalProperties": false }, - "interval": { - "description": "Per interval", + { "type": "string" } - }, - "additionalProperties": false, - "nullable": true - }, - "timeout": { - "description": "Enable timeout for incoming requests", - "default": null, - "type": "string" + ] } }, - "additionalProperties": false, - "nullable": true - }, - "deduplicate_variables": { - "description": "DEPRECATED, now always enabled: Enable variable deduplication optimization when sending requests to subgraphs (https://github.com/apollographql/router/issues/87)", - "default": null, - "type": "boolean", - "nullable": true + "additionalProperties": false }, - "router": { - "description": "Applied at the router level", + { + "description": "All sub-conditions must be true.", "type": "object", + "required": [ + "all" + ], "properties": { - "global_rate_limit": { - "description": "Enable global rate limiting", - "type": "object", - "required": [ - "capacity", - "interval" - ], - "properties": { - "capacity": { - "description": "Number of requests allowed", - "type": "integer", - "format": "uint64", - "minimum": 1.0 - }, - "interval": { - "description": "Per interval", - "type": "string" - } - }, - "additionalProperties": false, - "nullable": true - }, - "timeout": { - "description": "Enable timeout for incoming requests", - "default": null, - "type": "string" + "all": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } } }, - "additionalProperties": false, - "nullable": true + "additionalProperties": false }, - "subgraphs": { - "description": "Applied on specific subgraphs", + { + "description": "At least one sub-conditions must be true.", "type": "object", - "additionalProperties": { - "description": "Traffic shaping options", - "type": "object", - "properties": { - "compression": { - "description": "Enable compression for subgraphs (available compressions are deflate, br, gzip)", - "oneOf": [ - { - "description": "gzip", - "type": "string", - "enum": [ - "gzip" - ] - }, - { - "description": "deflate", - "type": "string", - "enum": [ - "deflate" - ] - }, - { - "description": "brotli", - "type": "string", - "enum": [ - "br" - ] - }, - { - "description": "identity", - "type": "string", - "enum": [ - "identity" - ] - } - ], - "nullable": true - }, - "deduplicate_query": { - "description": "Enable query deduplication", - "type": "boolean", - "nullable": true - }, - "experimental_http2": { - "description": "Enable HTTP2 for subgraphs", - "oneOf": [ - { - "description": "Enable HTTP2 for subgraphs", - "type": "string", - "enum": [ - "enable" - ] - }, - { - "description": "Disable HTTP2 for subgraphs", - "type": "string", - "enum": [ - "disable" - ] - }, - { - "description": "Only HTTP2 is active", - "type": "string", - "enum": [ - "http2only" - ] - } - ], - "nullable": true - }, - "experimental_retry": { - "description": "Retry configuration", - "type": "object", - "properties": { - "min_per_sec": { - "description": "minimum rate of retries allowed to accomodate clients that have just started issuing requests, or clients that do not issue many requests per window. The default value is 10", - "type": "integer", - "format": "uint32", - "minimum": 0.0, - "nullable": true - }, - "retry_mutations": { - "description": "allows request retries on mutations. This should only be activated if mutations are idempotent. Disabled by default", - "type": "boolean", - "nullable": true - }, - "retry_percent": { - "description": "percentage of calls to deposit that can be retried. This is in addition to any retries allowed for via min_per_sec. Must be between 0 and 1000, default value is 0.2", - "type": "number", - "format": "float", - "nullable": true - }, - "ttl": { - "description": "how long a single deposit should be considered. Must be between 1 and 60 seconds, default value is 10 seconds", - "default": null, - "type": "string" - } - }, - "additionalProperties": false, - "nullable": true - }, - "global_rate_limit": { - "description": "Enable global rate limiting", - "type": "object", - "required": [ - "capacity", - "interval" - ], - "properties": { - "capacity": { - "description": "Number of requests allowed", - "type": "integer", - "format": "uint64", - "minimum": 1.0 - }, - "interval": { - "description": "Per interval", - "type": "string" - } - }, - "additionalProperties": false, - "nullable": true - }, - "timeout": { - "description": "Enable timeout for incoming requests", - "default": null, - "type": "string" + "required": [ + "any" + ], + "properties": { + "any": { + "type": "array", + "items": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" } - }, - "additionalProperties": false - } + } + }, + "additionalProperties": false + }, + { + "description": "The sub-condition must not be true", + "type": "object", + "required": [ + "not" + ], + "properties": { + "not": { + "$ref": "#/definitions/Condition_for_SupergraphSelector" + } + }, + "additionalProperties": false + }, + { + "description": "Static true condition", + "type": "string", + "enum": [ + "true" + ] + }, + { + "description": "Static false condition", + "type": "string", + "enum": [ + "false" + ] } - }, - "additionalProperties": false + ] } } } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 0920ce78ad..a67ffceed0 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -253,15 +253,6 @@ impl Plugin for Telemetry { if config.instrumentation.spans.mode == SpanMode::Deprecated { ::tracing::warn!("telemetry.instrumentation.spans.mode is currently set to 'deprecated', either explicitly or via defaulting. Set telemetry.instrumentation.spans.mode explicitly in your router.yaml to 'spec_compliant' for log and span attributes that follow OpenTelemetry semantic conventions. This option will be defaulted to 'spec_compliant' in a future release and eventually removed altogether"); } - let public_meter_provider = Some(FilterMeterProvider::public( - metrics_builder.public_meter_provider_builder.build(), - )); - let private_meter_provider = Some(FilterMeterProvider::private( - metrics_builder.apollo_meter_provider_builder.build(), - )); - let public_prometheus_meter_provider = metrics_builder - .prometheus_meter_provider - .map(FilterMeterProvider::public); Ok(Telemetry { custom_endpoints: metrics_builder.custom_endpoints, @@ -2024,16 +2015,6 @@ mod tests { "status" = "200", "x-custom" = "coming_from_header" ); - assert_histogram_sum!( - "apollo_router_http_request_duration_seconds", - 1, - "another_test" = "my_default_value", - "my_value" = 2, - "myname" = "label_value", - "renamed_value" = "my_value_set", - "status" = "200", - "x-custom" = "coming_from_header" - ); } .with_metrics() .await; @@ -2539,15 +2520,6 @@ mod tests { "renamed_value" = "my_value_set", "status" = "400" ); - assert_histogram_sum!( - "apollo_router_http_request_duration_seconds", - 1, - "another_test" = "my_default_value", - "error" = "400 Bad Request", - "myname" = "label_value", - "renamed_value" = "my_value_set", - "status" = "400" - ); } .with_metrics() .await; From c7272775902e69719a01c76e6f4c2b6efa8aa107 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:50:04 +0100 Subject: [PATCH 10/25] refactor Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/conditions.rs | 2 - .../telemetry/config_new/instruments.rs | 292 ++++++------------ 2 files changed, 96 insertions(+), 198 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index d034d9178d..78418132a7 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -152,7 +152,6 @@ where fn on_request(&self, request: &T::Request) -> Option { match self { SelectorOrValue::Value(value) => Some(value.clone().into()), - // TODO return Some(null) ?! SelectorOrValue::Selector(selector) => selector.on_request(request), } } @@ -160,7 +159,6 @@ where fn on_response(&self, response: &T::Response) -> Option { match self { SelectorOrValue::Value(value) => Some(value.clone().into()), - // TODO return Some(null) ?! SelectorOrValue::Selector(selector) => selector.on_response(response), } } diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index c7056585a8..fd39792700 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -3,11 +3,9 @@ use std::collections::LinkedList; use std::fmt::Debug; use std::sync::Arc; -use http::header::CONTENT_LENGTH; use opentelemetry_api::metrics::Counter; use opentelemetry_api::metrics::Histogram; use opentelemetry_api::metrics::MeterProvider; -use opentelemetry_api::metrics::Unit; use opentelemetry_api::metrics::UpDownCounter; use opentelemetry_api::KeyValue; use opentelemetry_semantic_conventions::trace::HTTP_REQUEST_METHOD; @@ -38,6 +36,8 @@ use crate::services::subgraph; use crate::services::supergraph; use crate::Context; +const METER_NAME: &str = "apollo/router"; + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] @@ -58,7 +58,7 @@ pub(crate) struct InstrumentsConfig { impl InstrumentsConfig { pub(crate) fn new_router_instruments(&self) -> RouterInstruments { - let meter = metrics::meter_provider().meter("apollo/router"); + let meter = metrics::meter_provider().meter(METER_NAME); let http_server_request_duration = self .router .attributes @@ -83,46 +83,61 @@ impl InstrumentsConfig { .attributes .http_server_request_body_size .is_enabled() - .then(|| CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some(meter.f64_histogram("http.server.request.body.size").init()), - attributes: Vec::new(), - selector: Some(Arc::new(RouterSelector::RequestHeader { - request_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors: match &self.router.attributes.http_server_request_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - Some(attributes.clone()) - } - }, - }), + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.router.attributes.http_server_request_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.server.request.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(RouterSelector::RequestHeader { + request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), + } }); let http_server_response_body_size = self .router .attributes .http_server_response_body_size .is_enabled() - .then(|| CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some(meter.f64_histogram("http.server.response.body.size").init()), - attributes: Vec::new(), - selector: Some(Arc::new(RouterSelector::ResponseHeader { - response_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors: match &self.router.attributes.http_server_response_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - Some(attributes.clone()) - } - }, - }), + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.router.attributes.http_server_response_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.server.response.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(RouterSelector::ResponseHeader { + response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), + } }); let http_server_active_requests = self .router @@ -155,48 +170,60 @@ impl InstrumentsConfig { } pub(crate) fn new_subgraph_instruments(&self) -> SubgraphInstruments { - let meter = metrics::meter_provider().meter("apollo/router"); + let meter = metrics::meter_provider().meter(METER_NAME); let http_client_request_duration = self .subgraph .attributes .http_client_request_duration .is_enabled() - .then(|| CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Duration(Instant::now()), - histogram: Some(meter.f64_histogram("http.client.request.duration").init()), - attributes: Vec::new(), - selector: None, - selectors: match &self.subgraph.attributes.http_client_request_duration { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - Some(attributes.clone()) - } - }, - }), + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.subgraph.attributes.http_client_request_duration { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Duration(Instant::now()), + histogram: Some(meter.f64_histogram("http.client.request.duration").init()), + attributes: Vec::with_capacity(nb_attributes), + selector: None, + selectors, + }), + } }); let http_client_request_body_size = self .subgraph .attributes .http_client_request_body_size .is_enabled() - .then(|| CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some(meter.f64_histogram("http.client.request.body.size").init()), - attributes: Vec::new(), - selector: Some(Arc::new(SubgraphSelector::SubgraphRequestHeader { - subgraph_request_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors: match &self.subgraph.attributes.http_client_request_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - Some(attributes.clone()) - } - }, - }), + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.subgraph.attributes.http_client_request_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.client.request.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(SubgraphSelector::SubgraphRequestHeader { + subgraph_request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), + } }); let http_client_response_body_size = self .subgraph @@ -431,133 +458,6 @@ pub(crate) trait Instrumented { fn on_error(&self, error: &BoxError, ctx: &Context); } -struct SubgraphInstant(Instant); - -impl Instrumented for SubgraphInstrumentsConfig { - type Request = subgraph::Request; - type Response = subgraph::Response; - - fn on_request(&self, request: &Self::Request) { - let meter = metrics::meter_provider().meter("apollo/router"); - request - .context - .extensions() - .lock() - .insert(SubgraphInstant(Instant::now())); - if self.http_client_request_body_size.is_enabled() { - let body_size = request - .subgraph_request - .headers() - .get(&CONTENT_LENGTH) - .and_then(|val| val.to_str().ok()?.parse::().ok()); - if let Some(body_size) = body_size { - match meter - .u64_histogram("http.client.request.body.size") - .try_init() - { - Ok(histogram) => { - let attrs = self - .http_client_request_body_size - .on_request(request) - .into_iter() - .collect::>(); - histogram.record(body_size, &attrs); - } - Err(err) => { - tracing::error!( - "cannot create gauge for 'http.client.request.body.size': {err:?}" - ); - } - } - } - } - } - - fn on_response(&self, response: &Self::Response) { - let meter = metrics::meter_provider().meter("apollo/router"); - if self.http_client_request_duration.is_enabled() { - let attrs = self - .http_client_request_duration - .on_response(response) - .into_iter() - .collect::>(); - let request_duration = response - .context - .extensions() - .lock() - .get::() - .map(|i| i.0.elapsed()); - if let Some(request_duration) = request_duration { - match meter - .f64_histogram("http.client.request.duration") - .with_unit(Unit::new("s")) - .try_init() - { - Ok(histogram) => histogram.record(request_duration.as_secs_f64(), &attrs), - Err(err) => { - tracing::error!( - "cannot create histogram for 'http.client.request.duration': {err:?}" - ); - } - } - } - } - - if self.http_client_response_body_size.is_enabled() { - let body_size = response - .response - .headers() - .get(&CONTENT_LENGTH) - .and_then(|val| val.to_str().ok()?.parse::().ok()); - if let Some(body_size) = body_size { - match meter - .u64_histogram("http.client.response.body.size") - .try_init() - { - Ok(histogram) => { - let attrs = self - .http_client_response_body_size - .on_response(response) - .into_iter() - .collect::>(); - histogram.record(body_size, &attrs); - } - Err(err) => { - tracing::error!( - "cannot create histogram for 'http.client.response.body.size': {err:?}" - ); - } - } - } - } - } - - fn on_error(&self, error: &BoxError, ctx: &Context) { - let meter = metrics::meter_provider().meter("apollo/router"); - if self.http_client_request_duration.is_enabled() { - let attrs = self - .http_client_request_duration - .on_error(error) - .into_iter() - .collect::>(); - let request_duration = ctx - .extensions() - .lock() - .get::() - .map(|i| i.0.elapsed()); - if let Some(request_duration) = request_duration { - if let Ok(histogram) = meter - .f64_histogram("http.client.request.duration") - .with_unit(Unit::new("s")) - .try_init() - { - histogram.record(request_duration.as_secs_f64(), &attrs) - } - } - } - } -} - impl Instrumented for Extendable> where A: Default + Instrumented, @@ -626,7 +526,7 @@ where pub(crate) fn new(config: &HashMap>) -> Self { let mut counters = Vec::new(); let mut histograms = Vec::new(); - let meter = metrics::meter_provider().meter("apollo/router"); + let meter = metrics::meter_provider().meter(METER_NAME); for (instrument_name, instrument) in config { match instrument.ty { From 0f50dd8d1dcb36a031530db1925770d23373aa5e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:39:01 +0100 Subject: [PATCH 11/25] switch from LinkedList to Vec as the performance gain was not that big and with custom instruments it will be more compliant to use Vec for attributes Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/attributes.rs | 133 +++++++++--------- .../telemetry/config_new/extendable.rs | 7 +- .../telemetry/config_new/instruments.rs | 66 +++++---- .../src/plugins/telemetry/config_new/mod.rs | 8 +- .../plugins/telemetry/dynamic_attribute.rs | 8 +- .../src/plugins/telemetry/formatters/json.rs | 5 +- .../src/plugins/telemetry/formatters/mod.rs | 3 +- .../src/plugins/telemetry/formatters/text.rs | 5 +- apollo-router/src/plugins/telemetry/mod.rs | 7 +- 9 files changed, 119 insertions(+), 123 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index b3b62d304f..5a9bd2a7a9 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -1,4 +1,3 @@ -use std::collections::LinkedList; use std::fmt::Debug; use std::net::SocketAddr; @@ -532,12 +531,12 @@ impl Selectors for RouterAttributes { type Request = router::Request; type Response = router::Response; - fn on_request(&self, request: &router::Request) -> LinkedList { + fn on_request(&self, request: &router::Request) -> Vec { let mut attrs = self.common.on_request(request); attrs.extend(self.server.on_request(request)); if let Some(true) = &self.trace_id { if let Some(trace_id) = trace_id() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( Key::from_static_str("trace_id"), trace_id.to_string(), )); @@ -545,7 +544,7 @@ impl Selectors for RouterAttributes { } if let Some(true) = &self.datadog_trace_id { if let Some(trace_id) = trace_id() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( Key::from_static_str("dd.trace_id"), trace_id.to_datadog(), )); @@ -555,20 +554,20 @@ impl Selectors for RouterAttributes { let context = Span::current().context(); let baggage = context.baggage(); for (key, (value, _)) in baggage { - attrs.push_back(KeyValue::new(key.clone(), value.clone())); + attrs.push(KeyValue::new(key.clone(), value.clone())); } } attrs } - fn on_response(&self, response: &router::Response) -> LinkedList { + fn on_response(&self, response: &router::Response) -> Vec { let mut attrs = self.common.on_response(response); attrs.extend(self.server.on_response(response)); attrs } - fn on_error(&self, error: &BoxError) -> LinkedList { + fn on_error(&self, error: &BoxError) -> Vec { let mut attrs = self.common.on_error(error); attrs.extend(self.server.on_error(error)); attrs @@ -579,10 +578,10 @@ impl Selectors for HttpCommonAttributes { type Request = router::Request; type Response = router::Response; - fn on_request(&self, request: &router::Request) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_request(&self, request: &router::Request) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.http_request_method { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_REQUEST_METHOD, request.router_request.method().as_str().to_string(), )); @@ -595,7 +594,7 @@ impl Selectors for HttpCommonAttributes { .get(&CONTENT_LENGTH) .and_then(|h| h.to_str().ok()) { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_REQUEST_BODY_SIZE, content_length.to_string(), )); @@ -603,17 +602,17 @@ impl Selectors for HttpCommonAttributes { } if let Some(true) = &self.network_protocol_name { if let Some(scheme) = request.router_request.uri().scheme() { - attrs.push_back(KeyValue::new(NETWORK_PROTOCOL_NAME, scheme.to_string())); + attrs.push(KeyValue::new(NETWORK_PROTOCOL_NAME, scheme.to_string())); } } if let Some(true) = &self.network_protocol_version { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( NETWORK_PROTOCOL_VERSION, format!("{:?}", request.router_request.version()), )); } if let Some(true) = &self.network_transport { - attrs.push_back(KeyValue::new(NETWORK_TRANSPORT, "tcp".to_string())); + attrs.push(KeyValue::new(NETWORK_TRANSPORT, "tcp".to_string())); } if let Some(true) = &self.network_type { if let Some(connection_info) = @@ -621,9 +620,9 @@ impl Selectors for HttpCommonAttributes { { if let Some(socket) = connection_info.server_address { if socket.is_ipv4() { - attrs.push_back(KeyValue::new(NETWORK_TYPE, "ipv4".to_string())); + attrs.push(KeyValue::new(NETWORK_TYPE, "ipv4".to_string())); } else if socket.is_ipv6() { - attrs.push_back(KeyValue::new(NETWORK_TYPE, "ipv6".to_string())); + attrs.push(KeyValue::new(NETWORK_TYPE, "ipv6".to_string())); } } } @@ -632,8 +631,8 @@ impl Selectors for HttpCommonAttributes { attrs } - fn on_response(&self, response: &router::Response) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_response(&self, response: &router::Response) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.http_response_body_size { if let Some(content_length) = response .response @@ -641,7 +640,7 @@ impl Selectors for HttpCommonAttributes { .get(&CONTENT_LENGTH) .and_then(|h| h.to_str().ok()) { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_RESPONSE_BODY_SIZE, content_length.to_string(), )); @@ -649,7 +648,7 @@ impl Selectors for HttpCommonAttributes { } if let Some(true) = &self.http_response_status_code { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_RESPONSE_STATUS_CODE, response.response.status().as_u16() as i64, )); @@ -657,7 +656,7 @@ impl Selectors for HttpCommonAttributes { if let Some(true) = &self.error_type { if !response.response.status().is_success() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( ERROR_TYPE, response .response @@ -671,10 +670,10 @@ impl Selectors for HttpCommonAttributes { attrs } - fn on_error(&self, _error: &BoxError) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_error(&self, _error: &BoxError) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.error_type { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( ERROR_TYPE, StatusCode::INTERNAL_SERVER_ERROR .canonical_reason() @@ -682,7 +681,7 @@ impl Selectors for HttpCommonAttributes { )); } if let Some(true) = &self.http_response_status_code { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_RESPONSE_STATUS_CODE, StatusCode::INTERNAL_SERVER_ERROR.as_u16() as i64, )); @@ -696,33 +695,33 @@ impl Selectors for HttpServerAttributes { type Request = router::Request; type Response = router::Response; - fn on_request(&self, request: &router::Request) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_request(&self, request: &router::Request) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.http_route { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( HTTP_ROUTE, request.router_request.uri().path().to_string(), )); } if let Some(true) = &self.client_address { if let Some(forwarded) = Self::forwarded_for(request) { - attrs.push_back(KeyValue::new(CLIENT_ADDRESS, forwarded.ip().to_string())); + attrs.push(KeyValue::new(CLIENT_ADDRESS, forwarded.ip().to_string())); } else if let Some(connection_info) = request.router_request.extensions().get::() { if let Some(socket) = connection_info.peer_address { - attrs.push_back(KeyValue::new(CLIENT_ADDRESS, socket.ip().to_string())); + attrs.push(KeyValue::new(CLIENT_ADDRESS, socket.ip().to_string())); } } } if let Some(true) = &self.client_port { if let Some(forwarded) = Self::forwarded_for(request) { - attrs.push_back(KeyValue::new(CLIENT_PORT, forwarded.port() as i64)); + attrs.push(KeyValue::new(CLIENT_PORT, forwarded.port() as i64)); } else if let Some(connection_info) = request.router_request.extensions().get::() { if let Some(socket) = connection_info.peer_address { - attrs.push_back(KeyValue::new(CLIENT_PORT, socket.port() as i64)); + attrs.push(KeyValue::new(CLIENT_PORT, socket.port() as i64)); } } } @@ -731,23 +730,23 @@ impl Selectors for HttpServerAttributes { if let Some(forwarded) = Self::forwarded_host(request).and_then(|h| h.host().map(|h| h.to_string())) { - attrs.push_back(KeyValue::new(SERVER_ADDRESS, forwarded)); + attrs.push(KeyValue::new(SERVER_ADDRESS, forwarded)); } else if let Some(connection_info) = request.router_request.extensions().get::() { if let Some(socket) = connection_info.server_address { - attrs.push_back(KeyValue::new(SERVER_ADDRESS, socket.ip().to_string())); + attrs.push(KeyValue::new(SERVER_ADDRESS, socket.ip().to_string())); } } } if let Some(true) = &self.server_port { if let Some(forwarded) = Self::forwarded_host(request).and_then(|h| h.port_u16()) { - attrs.push_back(KeyValue::new(SERVER_PORT, forwarded as i64)); + attrs.push(KeyValue::new(SERVER_PORT, forwarded as i64)); } else if let Some(connection_info) = request.router_request.extensions().get::() { if let Some(socket) = connection_info.server_address { - attrs.push_back(KeyValue::new(SERVER_PORT, socket.port() as i64)); + attrs.push(KeyValue::new(SERVER_PORT, socket.port() as i64)); } } } @@ -757,7 +756,7 @@ impl Selectors for HttpServerAttributes { request.router_request.extensions().get::() { if let Some(socket) = connection_info.server_address { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( NETWORK_LOCAL_ADDRESS, socket.ip().to_string(), )); @@ -769,7 +768,7 @@ impl Selectors for HttpServerAttributes { request.router_request.extensions().get::() { if let Some(socket) = connection_info.server_address { - attrs.push_back(KeyValue::new(NETWORK_LOCAL_PORT, socket.port() as i64)); + attrs.push(KeyValue::new(NETWORK_LOCAL_PORT, socket.port() as i64)); } } } @@ -779,7 +778,7 @@ impl Selectors for HttpServerAttributes { request.router_request.extensions().get::() { if let Some(socket) = connection_info.peer_address { - attrs.push_back(KeyValue::new(NETWORK_PEER_ADDRESS, socket.ip().to_string())); + attrs.push(KeyValue::new(NETWORK_PEER_ADDRESS, socket.ip().to_string())); } } } @@ -788,23 +787,23 @@ impl Selectors for HttpServerAttributes { request.router_request.extensions().get::() { if let Some(socket) = connection_info.peer_address { - attrs.push_back(KeyValue::new(NETWORK_PEER_PORT, socket.port() as i64)); + attrs.push(KeyValue::new(NETWORK_PEER_PORT, socket.port() as i64)); } } } let router_uri = request.router_request.uri(); if let Some(true) = &self.url_path { - attrs.push_back(KeyValue::new(URL_PATH, router_uri.path().to_string())); + attrs.push(KeyValue::new(URL_PATH, router_uri.path().to_string())); } if let Some(true) = &self.url_query { if let Some(query) = router_uri.query() { - attrs.push_back(KeyValue::new(URL_QUERY, query.to_string())); + attrs.push(KeyValue::new(URL_QUERY, query.to_string())); } } if let Some(true) = &self.url_scheme { if let Some(scheme) = router_uri.scheme_str() { - attrs.push_back(KeyValue::new(URL_SCHEME, scheme.to_string())); + attrs.push(KeyValue::new(URL_SCHEME, scheme.to_string())); } } if let Some(true) = &self.user_agent_original { @@ -814,19 +813,19 @@ impl Selectors for HttpServerAttributes { .get(&USER_AGENT) .and_then(|h| h.to_str().ok()) { - attrs.push_back(KeyValue::new(USER_AGENT_ORIGINAL, user_agent.to_string())); + attrs.push(KeyValue::new(USER_AGENT_ORIGINAL, user_agent.to_string())); } } attrs } - fn on_response(&self, _response: &router::Response) -> LinkedList { - LinkedList::default() + fn on_response(&self, _response: &router::Response) -> Vec { + Vec::default() } - fn on_error(&self, _error: &BoxError) -> LinkedList { - LinkedList::default() + fn on_error(&self, _error: &BoxError) -> Vec { + Vec::default() } } @@ -872,11 +871,11 @@ impl Selectors for SupergraphAttributes { type Request = supergraph::Request; type Response = supergraph::Response; - fn on_request(&self, request: &supergraph::Request) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_request(&self, request: &supergraph::Request) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.graphql_document { if let Some(query) = &request.supergraph_request.body().query { - attrs.push_back(KeyValue::new(GRAPHQL_DOCUMENT, query.clone())); + attrs.push(KeyValue::new(GRAPHQL_DOCUMENT, query.clone())); } } if let Some(true) = &self.graphql_operation_name { @@ -885,7 +884,7 @@ impl Selectors for SupergraphAttributes { .get::<_, String>(OPERATION_NAME) .unwrap_or_default() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( GRAPHQL_OPERATION_NAME, operation_name.clone(), )); @@ -897,7 +896,7 @@ impl Selectors for SupergraphAttributes { .get::<_, String>(OPERATION_KIND) .unwrap_or_default() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( GRAPHQL_OPERATION_TYPE, operation_type.clone(), )); @@ -907,12 +906,12 @@ impl Selectors for SupergraphAttributes { attrs } - fn on_response(&self, _response: &supergraph::Response) -> LinkedList { - LinkedList::default() + fn on_response(&self, _response: &supergraph::Response) -> Vec { + Vec::default() } - fn on_error(&self, _error: &BoxError) -> LinkedList { - LinkedList::default() + fn on_error(&self, _error: &BoxError) -> Vec { + Vec::default() } } @@ -920,16 +919,16 @@ impl Selectors for SubgraphAttributes { type Request = subgraph::Request; type Response = subgraph::Response; - fn on_request(&self, request: &subgraph::Request) -> LinkedList { - let mut attrs = LinkedList::new(); + fn on_request(&self, request: &subgraph::Request) -> Vec { + let mut attrs = Vec::new(); if let Some(true) = &self.graphql_document { if let Some(query) = &request.subgraph_request.body().query { - attrs.push_back(KeyValue::new(SUBGRAPH_GRAPHQL_DOCUMENT, query.clone())); + attrs.push(KeyValue::new(SUBGRAPH_GRAPHQL_DOCUMENT, query.clone())); } } if let Some(true) = &self.graphql_operation_name { if let Some(op_name) = &request.subgraph_request.body().operation_name { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( SUBGRAPH_GRAPHQL_OPERATION_NAME, op_name.clone(), )); @@ -942,7 +941,7 @@ impl Selectors for SubgraphAttributes { .get::<_, String>(OPERATION_KIND) .unwrap_or_default() { - attrs.push_back(KeyValue::new( + attrs.push(KeyValue::new( SUBGRAPH_GRAPHQL_OPERATION_TYPE, operation_type.clone(), )); @@ -950,19 +949,19 @@ impl Selectors for SubgraphAttributes { } if let Some(true) = &self.subgraph_name { if let Some(subgraph_name) = &request.subgraph_name { - attrs.push_back(KeyValue::new(SUBGRAPH_NAME, subgraph_name.clone())); + attrs.push(KeyValue::new(SUBGRAPH_NAME, subgraph_name.clone())); } } attrs } - fn on_response(&self, _response: &subgraph::Response) -> LinkedList { - LinkedList::default() + fn on_response(&self, _response: &subgraph::Response) -> Vec { + Vec::default() } - fn on_error(&self, _error: &BoxError) -> LinkedList { - LinkedList::default() + fn on_error(&self, _error: &BoxError) -> Vec { + Vec::default() } } diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index f365c5fb28..797d7bb130 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -1,6 +1,5 @@ use std::any::type_name; use std::collections::HashMap; -use std::collections::LinkedList; use std::fmt::Debug; use opentelemetry::KeyValue; @@ -154,7 +153,7 @@ where type Request = Request; type Response = Response; - fn on_request(&self, request: &Self::Request) -> LinkedList { + fn on_request(&self, request: &Self::Request) -> Vec { let mut attrs = self.attributes.on_request(request); let custom_attributes = self.custom.iter().filter_map(|(key, value)| { value @@ -166,7 +165,7 @@ where attrs } - fn on_response(&self, response: &Self::Response) -> LinkedList { + fn on_response(&self, response: &Self::Response) -> Vec { let mut attrs = self.attributes.on_response(response); let custom_attributes = self.custom.iter().filter_map(|(key, value)| { value @@ -178,7 +177,7 @@ where attrs } - fn on_error(&self, error: &BoxError) -> LinkedList { + fn on_error(&self, error: &BoxError) -> Vec { self.attributes.on_error(error) } } diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index fd39792700..af3fd771c8 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::collections::LinkedList; use std::fmt::Debug; use std::sync::Arc; @@ -230,23 +229,30 @@ impl InstrumentsConfig { .attributes .http_client_response_body_size .is_enabled() - .then(|| CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some(meter.f64_histogram("http.client.response.body.size").init()), - attributes: Vec::new(), - selector: Some(Arc::new(SubgraphSelector::SubgraphResponseHeader { - subgraph_response_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors: match &self.subgraph.attributes.http_client_response_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - Some(attributes.clone()) - } - }, - }), + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.subgraph.attributes.http_client_response_body_size { + DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.client.response.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), + } }); SubgraphInstruments { http_client_request_duration, @@ -319,23 +325,23 @@ where type Request = Request; type Response = Response; - fn on_request(&self, request: &Self::Request) -> LinkedList { + fn on_request(&self, request: &Self::Request) -> Vec { match self { - Self::Bool(_) => LinkedList::new(), + Self::Bool(_) => Vec::new(), Self::Extendable { attributes } => attributes.on_request(request), } } - fn on_response(&self, response: &Self::Response) -> LinkedList { + fn on_response(&self, response: &Self::Response) -> Vec { match self { - Self::Bool(_) => LinkedList::new(), + Self::Bool(_) => Vec::new(), Self::Extendable { attributes } => attributes.on_response(response), } } - fn on_error(&self, error: &BoxError) -> LinkedList { + fn on_error(&self, error: &BoxError) -> Vec { match self { - Self::Bool(_) => LinkedList::new(), + Self::Bool(_) => Vec::new(), Self::Extendable { attributes } => attributes.on_error(error), } } @@ -404,15 +410,15 @@ where type Response = Response; - fn on_request(&self, request: &Self::Request) -> LinkedList { + fn on_request(&self, request: &Self::Request) -> Vec { self.attributes.on_request(request) } - fn on_response(&self, response: &Self::Response) -> LinkedList { + fn on_response(&self, response: &Self::Response) -> Vec { self.attributes.on_response(response) } - fn on_error(&self, error: &BoxError) -> LinkedList { + fn on_error(&self, error: &BoxError) -> Vec { self.attributes.on_error(error) } } @@ -484,7 +490,7 @@ impl Selectors for SubgraphInstrumentsConfig { type Request = subgraph::Request; type Response = subgraph::Response; - fn on_request(&self, request: &Self::Request) -> LinkedList { + fn on_request(&self, request: &Self::Request) -> Vec { let mut attrs = self.http_client_request_body_size.on_request(request); attrs.extend(self.http_client_request_duration.on_request(request)); attrs.extend(self.http_client_response_body_size.on_request(request)); @@ -492,7 +498,7 @@ impl Selectors for SubgraphInstrumentsConfig { attrs } - fn on_response(&self, response: &Self::Response) -> LinkedList { + fn on_response(&self, response: &Self::Response) -> Vec { let mut attrs = self.http_client_request_body_size.on_response(response); attrs.extend(self.http_client_request_duration.on_response(response)); attrs.extend(self.http_client_response_body_size.on_response(response)); @@ -500,7 +506,7 @@ impl Selectors for SubgraphInstrumentsConfig { attrs } - fn on_error(&self, error: &BoxError) -> LinkedList { + fn on_error(&self, error: &BoxError) -> Vec { let mut attrs = self.http_client_request_body_size.on_error(error); attrs.extend(self.http_client_request_duration.on_error(error)); attrs.extend(self.http_client_response_body_size.on_error(error)); diff --git a/apollo-router/src/plugins/telemetry/config_new/mod.rs b/apollo-router/src/plugins/telemetry/config_new/mod.rs index a1b0cb623c..1449b326cc 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -1,5 +1,3 @@ -use std::collections::LinkedList; - use opentelemetry::baggage::BaggageExt; use opentelemetry::trace::TraceContextExt; use opentelemetry::trace::TraceId; @@ -27,9 +25,9 @@ pub(crate) mod spans; pub(crate) trait Selectors { type Request; type Response; - fn on_request(&self, request: &Self::Request) -> LinkedList; - fn on_response(&self, response: &Self::Response) -> LinkedList; - fn on_error(&self, error: &BoxError) -> LinkedList; + fn on_request(&self, request: &Self::Request) -> Vec; + fn on_response(&self, response: &Self::Response) -> Vec; + fn on_error(&self, error: &BoxError) -> Vec; } pub(crate) trait Selector { diff --git a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs index cab03565a6..42cfa60703 100644 --- a/apollo-router/src/plugins/telemetry/dynamic_attribute.rs +++ b/apollo-router/src/plugins/telemetry/dynamic_attribute.rs @@ -1,5 +1,3 @@ -use std::collections::LinkedList; - use opentelemetry::Key; use opentelemetry::KeyValue; use tracing_opentelemetry::OtelData; @@ -13,16 +11,16 @@ use super::tracing::APOLLO_PRIVATE_PREFIX; #[derive(Debug, Default)] pub(crate) struct LogAttributes { - attributes: LinkedList, + attributes: Vec, } impl LogAttributes { - pub(crate) fn attributes(&self) -> &LinkedList { + pub(crate) fn attributes(&self) -> &Vec { &self.attributes } pub(crate) fn insert(&mut self, kv: KeyValue) { - self.attributes.push_back(kv); + self.attributes.push(kv); } pub(crate) fn extend(&mut self, other: impl IntoIterator) { diff --git a/apollo-router/src/plugins/telemetry/formatters/json.rs b/apollo-router/src/plugins/telemetry/formatters/json.rs index aa0b6b9e1b..c7f58b32d4 100644 --- a/apollo-router/src/plugins/telemetry/formatters/json.rs +++ b/apollo-router/src/plugins/telemetry/formatters/json.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::collections::LinkedList; use std::fmt; use std::io; @@ -27,7 +26,7 @@ use crate::plugins::telemetry::formatters::to_list; #[derive(Debug)] pub(crate) struct Json { config: JsonFormat, - resource: LinkedList<(String, serde_json::Value)>, + resource: Vec<(String, serde_json::Value)>, excluded_attributes: HashSet<&'static str>, } @@ -51,7 +50,7 @@ impl Default for Json { } } -struct SerializableResources<'a>(&'a LinkedList<(String, serde_json::Value)>); +struct SerializableResources<'a>(&'a Vec<(String, serde_json::Value)>); impl<'a> serde::ser::Serialize for SerializableResources<'a> { fn serialize(&self, serializer_o: Ser) -> Result diff --git a/apollo-router/src/plugins/telemetry/formatters/mod.rs b/apollo-router/src/plugins/telemetry/formatters/mod.rs index eaff3737b2..5a332d03bd 100644 --- a/apollo-router/src/plugins/telemetry/formatters/mod.rs +++ b/apollo-router/src/plugins/telemetry/formatters/mod.rs @@ -3,7 +3,6 @@ pub(crate) mod json; pub(crate) mod text; use std::collections::HashMap; -use std::collections::LinkedList; use std::fmt; use std::time::Instant; @@ -230,7 +229,7 @@ pub(crate) fn filter_metric_events(event: &tracing::Event<'_>) -> bool { }) } -pub(crate) fn to_list(resource: Resource) -> LinkedList<(String, serde_json::Value)> { +pub(crate) fn to_list(resource: Resource) -> Vec<(String, serde_json::Value)> { resource .into_iter() .map(|(k, v)| { diff --git a/apollo-router/src/plugins/telemetry/formatters/text.rs b/apollo-router/src/plugins/telemetry/formatters/text.rs index c17e6d730e..2a3fc43fa4 100644 --- a/apollo-router/src/plugins/telemetry/formatters/text.rs +++ b/apollo-router/src/plugins/telemetry/formatters/text.rs @@ -1,7 +1,6 @@ #[cfg(test)] use std::collections::BTreeMap; use std::collections::HashSet; -use std::collections::LinkedList; use std::fmt; use nu_ansi_term::Color; @@ -33,7 +32,7 @@ use crate::plugins::telemetry::tracing::APOLLO_PRIVATE_PREFIX; pub(crate) struct Text { #[allow(dead_code)] timer: SystemTime, - resource: LinkedList<(String, Value)>, + resource: Vec<(String, Value)>, config: TextFormat, excluded_attributes: HashSet<&'static str>, } @@ -261,7 +260,7 @@ impl Text { pub(crate) fn format_resource( &self, writer: &mut Writer, - resource: &LinkedList<(String, Value)>, + resource: &Vec<(String, Value)>, ) -> fmt::Result { if !resource.is_empty() { let style = Style::new().dimmed(); diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index a67ffceed0..f0f982d46d 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -1,7 +1,6 @@ //! Telemetry plugin. use std::collections::BTreeMap; use std::collections::HashMap; -use std::collections::LinkedList; use std::fmt; use std::sync::Arc; use std::time::Duration; @@ -375,7 +374,7 @@ impl Plugin for Telemetry { ) }, move |(custom_attributes, custom_instruments, ctx): ( - LinkedList, + Vec, RouterInstruments, Context, ), @@ -526,7 +525,7 @@ impl Plugin for Telemetry { (req.context.clone(), custom_instruments, custom_attributes) }, - move |(ctx, custom_instruments, custom_attributes): (Context, SupergraphCustomInstruments, LinkedList), fut| { + move |(ctx, custom_instruments, custom_attributes): (Context, SupergraphCustomInstruments, Vec), fut| { let config = config_map_res.clone(); let sender = metrics_sender.clone(); let start = Instant::now(); @@ -629,7 +628,7 @@ impl Plugin for Telemetry { move |(context, custom_instruments, custom_attributes): ( Context, SubgraphInstruments, - LinkedList, + Vec, ), f: BoxFuture<'static, Result>| { let subgraph_attribute = subgraph_attribute.clone(); From a15279b2992b762a0af76597113ac91627b656a9 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:15:02 +0100 Subject: [PATCH 12/25] wrap selectors into an Arc Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../src/plugins/telemetry/config_new/extendable.rs | 7 +++++++ .../src/plugins/telemetry/config_new/instruments.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index 797d7bb130..f8a81daa39 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -1,6 +1,7 @@ use std::any::type_name; use std::collections::HashMap; use std::fmt::Debug; +use std::sync::Arc; use opentelemetry::KeyValue; use schemars::gen::SchemaGenerator; @@ -49,6 +50,12 @@ impl Extendable<(), ()> { { Default::default() } + pub(crate) fn empty_arc() -> Arc> + where + A: Default, + { + Default::default() + } } /// Custom Deserializer for attributes that will deserializse into a custom field if possible, but otherwise into one of the pre-defined attributes. diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index af3fd771c8..83266c7fbb 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -300,7 +300,7 @@ pub(crate) struct ActiveRequestsAttributes { #[serde(deny_unknown_fields, untagged)] enum DefaultedStandardInstrument { Bool(bool), - Extendable { attributes: T }, + Extendable { attributes: Arc }, } impl Default for DefaultedStandardInstrument { @@ -393,8 +393,8 @@ where unit: String, /// Attributes to include on the instrument. - #[serde(default = "Extendable::empty::")] - attributes: Extendable, + #[serde(default = "Extendable::empty_arc::")] + attributes: Arc>, /// The instrument conditions. #[serde(default = "Condition::empty::")] @@ -810,7 +810,7 @@ where { increment: Increment, selector: Option>, - selectors: Extendable, + selectors: Arc>, counter: Option>, condition: Condition, attributes: Vec, @@ -916,7 +916,7 @@ struct ActiveRequestsCounter { struct ActiveRequestsCounterInner { counter: Option>, - attrs_config: ActiveRequestsAttributes, + attrs_config: Arc, attributes: Vec, } @@ -1008,7 +1008,7 @@ where { increment: Increment, selector: Option>, - selectors: Option>, + selectors: Option>>, histogram: Option>, attributes: Vec, } From 7258849f9e1766e99b5d0471c5adacd014f93d4d Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:09:36 +0100 Subject: [PATCH 13/25] fix a bug with views Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/plugins/telemetry/config.rs | 8 ++++---- ...prometheus_custom_buckets_specific_metrics.router.yaml | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config.rs b/apollo-router/src/plugins/telemetry/config.rs index 61a127c789..a3725a6d3f 100644 --- a/apollo-router/src/plugins/telemetry/config.rs +++ b/apollo-router/src/plugins/telemetry/config.rs @@ -167,14 +167,14 @@ impl TryInto> for MetricView { record_min_max: true, }, ); - let mut instrument = Instrument::new().name(self.name); + let instrument = Instrument::new().name(self.name); + let mut mask = Stream::new(); if let Some(desc) = self.description { - instrument = instrument.description(desc); + mask = mask.description(desc); } if let Some(unit) = self.unit { - instrument = instrument.unit(Unit::new(unit)); + mask = mask.unit(Unit::new(unit)); } - let mut mask = Stream::new(); if let Some(aggregation) = aggregation { mask = mask.aggregation(aggregation); } diff --git a/apollo-router/src/plugins/telemetry/testdata/prometheus_custom_buckets_specific_metrics.router.yaml b/apollo-router/src/plugins/telemetry/testdata/prometheus_custom_buckets_specific_metrics.router.yaml index 06a868748d..c24056770f 100644 --- a/apollo-router/src/plugins/telemetry/testdata/prometheus_custom_buckets_specific_metrics.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/prometheus_custom_buckets_specific_metrics.router.yaml @@ -8,6 +8,8 @@ telemetry: service_name: apollo-router views: - name: apollo_router_http_request_duration_seconds + unit: seconds + description: duration of the http request aggregation: histogram: buckets: From 1e366f9fe5a2b8bfac5fdeac28f405058288c205 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:14:26 +0100 Subject: [PATCH 14/25] update docs Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../exporters/metrics/prometheus.mdx | 2 +- .../telemetry/instrumentation/conditions.mdx | 14 +++++++++++- .../telemetry/instrumentation/instruments.mdx | 22 ++++++++----------- .../telemetry/instrumentation/selectors.mdx | 4 ++-- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/docs/source/configuration/telemetry/exporters/metrics/prometheus.mdx b/docs/source/configuration/telemetry/exporters/metrics/prometheus.mdx index ab3f23781d..e22482dcd3 100644 --- a/docs/source/configuration/telemetry/exporters/metrics/prometheus.mdx +++ b/docs/source/configuration/telemetry/exporters/metrics/prometheus.mdx @@ -6,7 +6,7 @@ description: Configure the Prometheus metrics exporter endpoint in the Apollo Ro Enable and configure the [Prometheus](https://www.prometheus.io/) exporter for metrics in the Apollo Router. -For general tracing configuration, refer to [Router Metrics Configuration](./overview). +For general metrics configuration, refer to [Router Metrics Configuration](./overview). ## Prometheus configuration diff --git a/docs/source/configuration/telemetry/instrumentation/conditions.mdx b/docs/source/configuration/telemetry/instrumentation/conditions.mdx index 59d4f4a997..4a481a7af0 100644 --- a/docs/source/configuration/telemetry/instrumentation/conditions.mdx +++ b/docs/source/configuration/telemetry/instrumentation/conditions.mdx @@ -6,7 +6,7 @@ description: Set conditions for when events or instruments are triggered in the -You can set conditions for when an [instrument](./instruments) should be mutated or an [event](./events) should be triggered. +You can set conditions for when an [instrument](./instruments) should be mutated should be triggered. ## Condition configuration @@ -31,6 +31,17 @@ telemetry: - response_header: x-resp-header ``` +#### `exist` + +The `exist` condition is testing if [selectors](./selectors) is present. + +For example, the following condition checks the value of `x-req-header` exists: + +```yaml +exist: + - request_header: x-req-header +``` + #### `eq` The `eq` condition is an equality test between [selectors](./selectors) or values. @@ -105,6 +116,7 @@ The available basic conditions: | Condition | Description | |----------|----------------------------------------------------------| | `eq` | An equality test between selectors or values | +| `exist` | A check to see if the selectors value exists | | `not` | A negated equality test between selectors or values | | `all` | A list of conditions that must all be true | | `any` | A list of conditions of which at least one must be true | diff --git a/docs/source/configuration/telemetry/instrumentation/instruments.mdx b/docs/source/configuration/telemetry/instrumentation/instruments.mdx index cfd3cbce8f..799960a68b 100644 --- a/docs/source/configuration/telemetry/instrumentation/instruments.mdx +++ b/docs/source/configuration/telemetry/instrumentation/instruments.mdx @@ -6,10 +6,11 @@ description: Create and customize instruments to collect data and report measure import RouterServices from '../../../../shared/router-lifecycle-services.mdx'; + + An **instrument** is used to collect data and report measurements to a metric backend. The Apollo Router supports the following metric instruments: * Counter -* Gauge * Histogram Instruments in the router are configurable in `router.yaml` as `telemetry.instruments`. @@ -90,7 +91,6 @@ telemetry: supergraph: acme.graphql.requests: value: unit - event: on_error type: counter unit: count description: "supergraph requests" @@ -98,7 +98,6 @@ telemetry: subgraph: acme.graphql.subgraph.errors: value: unit - event: on_error type: counter unit: count description: "my description" @@ -184,7 +183,6 @@ The `value` of an instrument is the value which will be drawn from. This can be * `duration` - the duration of the pipeline service. * `unit` - the number of times the pipeline service has been executed. -* `active` - the number of in-progress requests in the pipeline service. * `custom` - a custom value extracted from the pipeline service. See [selectors](./selectors) for more information. ```yaml title="future.router.yaml" @@ -219,11 +217,9 @@ The value must be of the expected type for the instrument. For example, a counte #### `type` -Instruments come in four different types: +Instruments come in two different types: * `counter` - A monotonic counter. For example, requests served, tasks completed, or errors occurred. -* `up_down_counter` - A bidirectional counter that is modified by a delta. For example, the number of items in a queue. -* `gauge` - A value that is sampled at a specific point in time. For example, active requests or CPU usage. * `histogram` - A histogram of values. For example, request durations or response body sizes. ```yaml title="future.router.yaml" @@ -233,7 +229,7 @@ telemetry: router: acme.metric: # ... - type: counter # counter, up_down_counter, gauge, histogram + type: counter # counter, histogram ``` #### `unit` @@ -321,11 +317,11 @@ telemetry: |---------------------------------------|------------------------------------------------------------------------------|------------|----------------------------------------------| | `` | | | The name of the custom attribute. | | `` | | | The name of the custom instrument. | -| `attributes` | [standard attributes](./standard-attributes) or [selectors](./selectors) | | The attributes of the custom instrument. | -| `condition` | [conditions](./conditions) | | The a condition for mutating the instrument. | +| `attributes` | [standard attributes](./standard-attributes) or [selectors](./selectors) | | The attributes of the custom instrument. | +| `condition` | [conditions](./conditions) | | The a condition for mutating the instrument. | | `default_attribute_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | -| `type` | `counter`\|`up_down_counter`\|`gauge`\|`histogram` | | The name of the custom instrument. | -| `unit` | | | A unit name, for example `By` or `{request}`. | +| `type` | `counter`\|`histogram` | | The name of the custom instrument. | +| `unit` | | | A unit name, for example `By` or `{request}`.| | `description` | | | The description of the custom instrument. | -| `value` | `unit`\|`duration`\|`active`\|`` | | The value of the instrument. | +| `value` | `unit`\|`duration`\|`` | | The value of the instrument. | diff --git a/docs/source/configuration/telemetry/instrumentation/selectors.mdx b/docs/source/configuration/telemetry/instrumentation/selectors.mdx index d2749e59a2..504ff0e5ee 100644 --- a/docs/source/configuration/telemetry/instrumentation/selectors.mdx +++ b/docs/source/configuration/telemetry/instrumentation/selectors.mdx @@ -5,7 +5,7 @@ description: Extract and select data from the Apollo Router's pipeline services --- import RouterServices from '../../../../shared/router-lifecycle-services.mdx'; -A **selector** is used to extract data from the Apollo Router's request lifecycle (pipeline) services and attach them to telemetry, specifically [spans](./spans). +A **selector** is used to extract data from the Apollo Router's request lifecycle (pipeline) services and attach them to telemetry, specifically [spans](./spans), [instruments](./instruments), [conditions](./conditions). Each service of the router pipeline (`router`, `supergraph`, `subgraph`) has its own available selectors. @@ -73,7 +73,7 @@ The subgraph service executes multiple times during query execution, with each e | `subgraph_response_errors` | Yes | | Json Path into the subgraph response body errors (it might impact performances) | | `subgraph_request_header` | Yes | | The name of a subgraph request header | | `subgraph_response_header` | Yes | | The name of a subgraph response header | -| `subgraph_response_status` | Yes | | The name of a subgraph response header | +| `subgraph_response_status` | Yes | | The status of a subgraph response | | `supergraph_operation_name` | Yes | | The operation name from the supergraph query | | `supergraph_operation_kind` | Yes | `query`\|`mutation`\|`subscription` | The operation kind from the supergraph query | | `supergraph_query` | Yes | | The graphql query to the supergraph | From 83bb542a6091d05a717116b0a81a5b04330996de Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:32:50 +0100 Subject: [PATCH 15/25] add metrics Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/configuration/metrics.rs | 8 ++++++++ ...ion__metrics__test__metrics@telemetry.router.yaml.snap | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/apollo-router/src/configuration/metrics.rs b/apollo-router/src/configuration/metrics.rs index 8d42c9b1ef..093675a010 100644 --- a/apollo-router/src/configuration/metrics.rs +++ b/apollo-router/src/configuration/metrics.rs @@ -307,6 +307,14 @@ impl InstrumentData { "$..events", opt.instruments, "$..instruments", + opt.instruments.router, + "$..instruments.router", + opt.instruments.supergraph, + "$..instruments.supergraph", + opt.instruments.subgraph, + "$..instruments.subgraph", + opt.instruments.default_attribute_requirement_level, + "$..instruments.default_attribute_requirement_level", opt.spans, "$..spans", opt.spans.mode, diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap index 0e904afcdc..063be84f7f 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap @@ -9,6 +9,10 @@ expression: "&metrics.non_zero()" attributes: opt.events: false opt.instruments: false + opt.instruments.default_attribute_requirement_level: false + opt.instruments.router: false + opt.instruments.subgraph: false + opt.instruments.supergraph: false opt.logging.experimental_when_header: true opt.metrics.otlp: true opt.metrics.prometheus: true From 28e90635b9a8b6ac861f9c7c26eabcda18f0ae74 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:07:29 +0100 Subject: [PATCH 16/25] add unit test for router instrument Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 159 +++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 83266c7fbb..2fdb4de15b 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -1039,7 +1039,6 @@ where .map(|s| s.on_response(response).into_iter().collect()) .unwrap_or_default(); attrs.append(&mut inner.attributes); - if let Some(selected_value) = inner .selector .as_ref() @@ -1103,3 +1102,161 @@ where } } } + +#[cfg(test)] +mod tests { + use http::StatusCode; + use serde_json::json; + + use super::*; + use crate::metrics::FutureMetricsExt; + use crate::services::RouterRequest; + use crate::services::RouterResponse; + + #[tokio::test] + async fn test_router_instruments() { + async { + let config: InstrumentsConfig = serde_json::from_str( + json!({ + "router": { + "http.server.request.body.size": true, + "http.server.response.body.size": { + "attributes": { + "http.response.status_code": false, + "acme.my_attribute": { + "response_header": "x-my-header", + "default": "unknown" + } + } + }, + "acme.request.on_error": { + "value": "unit", + "type": "counter", + "unit": "error", + "description": "my description", + "condition": { + "not": { + "eq": [ + 200, + { + "response_status": "code" + } + ] + } + }, + "attributes": { + "http.response.status_code": true + } + }, + "acme.request.header_value": { + "value": { + "request_header": "x-my-header-count" + }, + "type": "counter", + "description": "my description", + "unit": "nb" + } + } + }) + .to_string() + .as_str(), + ) + .unwrap(); + + let router_instruments = config.new_router_instruments(); + let router_req = RouterRequest::fake_builder() + .header("conditional-custom", "X") + .header("x-my-header-count", "55") + .header("content-length", "35") + .header("content-type", "application/graphql") + .build() + .unwrap(); + router_instruments.on_request(&router_req); + let router_response = RouterResponse::fake_builder() + .context(router_req.context.clone()) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .header("x-my-header", "TEST") + .header("content-length", "35") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap(); + router_instruments.on_response(&router_response); + + assert_counter!("acme.request.header_value", 55.0); + assert_counter!( + "acme.request.on_error", + 1.0, + "http.response.status_code" = 400 + ); + assert_histogram_sum!("http.server.request.body.size", 35.0); + assert_histogram_sum!( + "http.server.response.body.size", + 35.0, + "acme.my_attribute" = "TEST" + ); + + let router_instruments = config.new_router_instruments(); + let router_req = RouterRequest::fake_builder() + .header("content-length", "35") + .header("x-my-header-count", "5") + .header("content-type", "application/graphql") + .build() + .unwrap(); + router_instruments.on_request(&router_req); + let router_response = RouterResponse::fake_builder() + .context(router_req.context.clone()) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .header("content-length", "35") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap(); + router_instruments.on_response(&router_response); + + assert_counter!("acme.request.header_value", 60.0); + assert_counter!( + "acme.request.on_error", + 2.0, + "http.response.status_code" = 400 + ); + assert_histogram_sum!("http.server.request.body.size", 70.0); + assert_histogram_sum!( + "http.server.response.body.size", + 35.0, + "acme.my_attribute" = "TEST" + ); + assert_histogram_sum!( + "http.server.response.body.size", + 35.0, + "acme.my_attribute" = "unknown" + ); + + let router_instruments = config.new_router_instruments(); + let router_req = RouterRequest::fake_builder() + .header("content-length", "35") + .header("content-type", "application/graphql") + .build() + .unwrap(); + router_instruments.on_request(&router_req); + let router_response = RouterResponse::fake_builder() + .context(router_req.context.clone()) + .status_code(StatusCode::OK) + .header("content-type", "application/json") + .header("content-length", "35") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap(); + router_instruments.on_response(&router_response); + + assert_counter!("acme.request.header_value", 60.0); + assert_counter!( + "acme.request.on_error", + 2.0, + "http.response.status_code" = 400 + ); + } + .with_metrics() + .await; + } +} From 52dde1141df3f46051b0be86de8e70134b612d0e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:34:25 +0100 Subject: [PATCH 17/25] add unit test for supergraph instruments Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .../telemetry/config_new/instruments.rs | 139 +++++++++++++++++- .../plugins/telemetry/config_new/selectors.rs | 17 ++- .../telemetry/instrumentation/selectors.mdx | 8 +- 3 files changed, 157 insertions(+), 7 deletions(-) diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 2fdb4de15b..222bdefc6a 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -892,7 +892,7 @@ where T: Selector + Debug, { fn drop(&mut self) { - // TODO add attribute error broken pipe ? + // TODO add attribute error broken pipe ? cf https://github.com/apollographql/router/issues/4866 let inner = self.inner.try_lock(); if let Some(mut inner) = inner { if let Some(counter) = inner.counter.take() { @@ -1085,7 +1085,7 @@ where T: Selector, { fn drop(&mut self) { - // TODO add attribute error broken pipe ? + // TODO add attribute error broken pipe ? cf https://github.com/apollographql/router/issues/4866 let inner = self.inner.try_lock(); if let Some(mut inner) = inner { if let Some(histogram) = inner.histogram.take() { @@ -1109,6 +1109,8 @@ mod tests { use serde_json::json; use super::*; + use crate::context::OPERATION_KIND; + use crate::graphql; use crate::metrics::FutureMetricsExt; use crate::services::RouterRequest; use crate::services::RouterResponse; @@ -1259,4 +1261,137 @@ mod tests { .with_metrics() .await; } + + #[tokio::test] + async fn test_supergraph_instruments() { + async { + let config: InstrumentsConfig = serde_json::from_str( + json!({ + "supergraph": { + "acme.request.on_error": { + "value": "unit", + "type": "counter", + "unit": "error", + "description": "my description", + "condition": { + "not": { + "eq": [ + 200, + { + "response_status": "code" + } + ] + } + } + }, + "acme.query": { + "value": "unit", + "type": "counter", + "description": "nb of queries", + "condition": { + "eq": [ + "query", + { + "operation_kind": "string" + } + ] + }, + "unit": "query", + "attributes": { + "query": { + "query": "string" + } + } + } + } + }) + .to_string() + .as_str(), + ) + .unwrap(); + + let custom_instruments = SupergraphCustomInstruments::new(&config.supergraph.custom); + let context = crate::context::Context::new(); + let _ = context.insert(OPERATION_KIND, "query".to_string()); + let supergraph_req = supergraph::Request::fake_builder() + .header("conditional-custom", "X") + .header("x-my-header-count", "55") + .header("content-length", "35") + .header("content-type", "application/graphql") + .query("{me{name}}") + .context(context.clone()) + .build() + .unwrap(); + custom_instruments.on_request(&supergraph_req); + let supergraph_response = supergraph::Response::fake_builder() + .context(supergraph_req.context.clone()) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .header("x-my-header", "TEST") + .header("content-length", "35") + .errors(vec![graphql::Error::builder() + .message("nope") + .extension_code("NOPE") + .build()]) + .build() + .unwrap(); + custom_instruments.on_response(&supergraph_response); + + assert_counter!("acme.query", 1.0, query = "{me{name}}"); + assert_counter!("acme.request.on_error", 1.0); + + let custom_instruments = SupergraphCustomInstruments::new(&config.supergraph.custom); + let supergraph_req = supergraph::Request::fake_builder() + .header("content-length", "35") + .header("x-my-header-count", "5") + .header("content-type", "application/graphql") + .context(context.clone()) + .query("Subscription {me{name}}") + .build() + .unwrap(); + custom_instruments.on_request(&supergraph_req); + let supergraph_response = supergraph::Response::fake_builder() + .context(supergraph_req.context.clone()) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .header("content-length", "35") + .errors(vec![graphql::Error::builder() + .message("nope") + .extension_code("NOPE") + .build()]) + .build() + .unwrap(); + custom_instruments.on_response(&supergraph_response); + + assert_counter!("acme.query", 1.0, query = "{me{name}}"); + assert_counter!("acme.request.on_error", 2.0); + + let custom_instruments = SupergraphCustomInstruments::new(&config.supergraph.custom); + let supergraph_req = supergraph::Request::fake_builder() + .header("content-length", "35") + .header("content-type", "application/graphql") + .context(context.clone()) + .query("{me{name}}") + .build() + .unwrap(); + custom_instruments.on_request(&supergraph_req); + let supergraph_response = supergraph::Response::fake_builder() + .context(supergraph_req.context.clone()) + .status_code(StatusCode::OK) + .header("content-type", "application/json") + .header("content-length", "35") + .errors(vec![graphql::Error::builder() + .message("nope") + .extension_code("NOPE") + .build()]) + .build() + .unwrap(); + custom_instruments.on_response(&supergraph_response); + + assert_counter!("acme.query", 2.0, query = "{me{name}}"); + assert_counter!("acme.request.on_error", 2.0); + } + .with_metrics() + .await; + } } diff --git a/apollo-router/src/plugins/telemetry/config_new/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/selectors.rs index 66801cf006..300905d96a 100644 --- a/apollo-router/src/plugins/telemetry/config_new/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/selectors.rs @@ -98,7 +98,7 @@ pub(crate) enum RouterSelector { /// Optional default value. default: Option, }, - /// A header from the response + /// A status from the response ResponseStatus { /// The http response status code. response_status: ResponseStatus, @@ -206,6 +206,11 @@ pub(crate) enum SupergraphSelector { /// Optional default value. default: Option, }, + /// A status from the response + ResponseStatus { + /// The http response status code. + response_status: ResponseStatus, + }, RequestContext { /// The request context key. request_context: String, @@ -626,6 +631,16 @@ impl Selector for SupergraphSelector { .and_then(|h| Some(h.to_str().ok()?.to_string())) .or_else(|| default.clone()) .map(opentelemetry::Value::from), + SupergraphSelector::ResponseStatus { response_status } => match response_status { + ResponseStatus::Code => Some(opentelemetry::Value::I64( + response.response.status().as_u16() as i64, + )), + ResponseStatus::Reason => response + .response + .status() + .canonical_reason() + .map(|reason| reason.to_string().into()), + }, SupergraphSelector::ResponseContext { response_context, default, diff --git a/docs/source/configuration/telemetry/instrumentation/selectors.mdx b/docs/source/configuration/telemetry/instrumentation/selectors.mdx index 504ff0e5ee..5af6dc1bac 100644 --- a/docs/source/configuration/telemetry/instrumentation/selectors.mdx +++ b/docs/source/configuration/telemetry/instrumentation/selectors.mdx @@ -47,8 +47,8 @@ The supergraph service is executed after query parsing but before query executio | Selector | Defaultable | Values | Description | |--------------------|-------------|-------------------------------------|--------------------------------------| | `operation_name` | Yes | | The operation name from the query | -| `operation_kind` | No | `query`\|`mutation`\|`subscription` | The operation kind from the query | -| `query` | Yes | `query`\|`hash` | The graphql query | +| `operation_kind` | No | `string` | The operation kind from the query | +| `query` | Yes | `string` | The graphql query | | `query_variable` | Yes | | The name of a graphql query variable | | `response_body` | Yes | | Json Path into the response body | | `request_header` | Yes | | The name of a request header | @@ -66,7 +66,7 @@ The subgraph service executes multiple times during query execution, with each e | Selector | Defaultable | Values | Description | |-----------------------------|-------------|-------------------------------------|---------------------------------------------------------------------------------| | `subgraph_operation_name` | Yes | | The operation name from the subgraph query | -| `subgraph_operation_kind` | No | `query`\|`mutation`\|`subscription` | The operation kind from the subgraph query | +| `subgraph_operation_kind` | No | `string` | The operation kind from the subgraph query | | `subgraph_query` | Yes | | The graphql query to the subgraph | | `subgraph_query_variable` | Yes | | The name of a subgraph query variable | | `subgraph_response_data` | Yes | | Json Path into the subgraph response body data (it might impact performances) | @@ -75,7 +75,7 @@ The subgraph service executes multiple times during query execution, with each e | `subgraph_response_header` | Yes | | The name of a subgraph response header | | `subgraph_response_status` | Yes | | The status of a subgraph response | | `supergraph_operation_name` | Yes | | The operation name from the supergraph query | -| `supergraph_operation_kind` | Yes | `query`\|`mutation`\|`subscription` | The operation kind from the supergraph query | +| `supergraph_operation_kind` | Yes | `string` | The operation kind from the supergraph query | | `supergraph_query` | Yes | | The graphql query to the supergraph | | `supergraph_query_variable` | Yes | | The name of a supergraph query variable | | `request_context` | Yes | | The name of a request context key | From b536a0771ac5c9fe8955cd897add206c83e8f7b6 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:15:55 +0100 Subject: [PATCH 18/25] add support of default_attribute_requirement_level Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 247 ++++++++++++++++-- apollo-router/src/metrics/mod.rs | 79 +++++- .../telemetry/config_new/conditions.rs | 15 +- .../telemetry/config_new/instruments.rs | 51 ++++ apollo-router/src/plugins/telemetry/mod.rs | 3 + .../testdata/custom_instruments.router.yaml | 6 +- .../telemetry/instrumentation/conditions.mdx | 6 +- 7 files changed, 368 insertions(+), 39 deletions(-) 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 de95a084ee..75621ffcc3 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 @@ -7012,7 +7012,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -7563,7 +7563,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -8114,7 +8114,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -8663,7 +8663,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -9127,7 +9127,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -9366,10 +9366,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "description": "A header from the request", @@ -9532,7 +9532,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -10023,7 +10023,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -14742,10 +14742,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "type": "object", @@ -16764,6 +16764,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -17267,6 +17296,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -17518,10 +17576,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "type": "object", @@ -17711,6 +17769,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -18241,6 +18328,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -18847,7 +18963,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -20184,6 +20300,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -21128,7 +21273,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -21367,10 +21512,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "description": "A header from the request", @@ -21533,7 +21678,7 @@ expression: "&schema" "additionalProperties": false }, { - "description": "A header from the response", + "description": "A status from the response", "type": "object", "required": [ "response_status" @@ -22752,10 +22897,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "type": "object", @@ -23925,6 +24070,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ @@ -24176,10 +24350,10 @@ expression: "&schema" "description": "A condition to check a selection against a selector.", "type": "object", "required": [ - "exist" + "exists" ], "properties": { - "exist": { + "exists": { "anyOf": [ { "type": "object", @@ -24369,6 +24543,35 @@ expression: "&schema" }, "additionalProperties": false }, + { + "description": "A status from the response", + "type": "object", + "required": [ + "response_status" + ], + "properties": { + "response_status": { + "description": "The http response status code.", + "oneOf": [ + { + "description": "The http status code.", + "type": "string", + "enum": [ + "code" + ] + }, + { + "description": "The http status reason.", + "type": "string", + "enum": [ + "reason" + ] + } + ] + } + }, + "additionalProperties": false + }, { "type": "object", "required": [ diff --git a/apollo-router/src/metrics/mod.rs b/apollo-router/src/metrics/mod.rs index 5f8a06e1fa..23a914c11f 100644 --- a/apollo-router/src/metrics/mod.rs +++ b/apollo-router/src/metrics/mod.rs @@ -185,19 +185,19 @@ pub(crate) mod test_utils { ) -> bool { let attributes = AttributeSet::from(attributes); if let Some(value) = value.to_u64() { - if self.metric_exists(name, &ty, value, &attributes) { + if self.metric_matches(name, &ty, value, &attributes) { return true; } } if let Some(value) = value.to_i64() { - if self.metric_exists(name, &ty, value, &attributes) { + if self.metric_matches(name, &ty, value, &attributes) { return true; } } if let Some(value) = value.to_f64() { - if self.metric_exists(name, &ty, value, &attributes) { + if self.metric_matches(name, &ty, value, &attributes) { return true; } } @@ -205,7 +205,7 @@ pub(crate) mod test_utils { false } - fn metric_exists( + pub(crate) fn metric_matches( &self, name: &str, ty: &MetricType, @@ -240,6 +240,44 @@ pub(crate) mod test_utils { false } + pub(crate) fn metric_exists( + &self, + name: &str, + ty: MetricType, + attributes: &[KeyValue], + ) -> bool { + let attributes = AttributeSet::from(attributes); + if let Some(metric) = self.find(name) { + // Try to downcast the metric to each type of aggregation and assert that the value is correct. + if let Some(gauge) = metric.data.as_any().downcast_ref::>() { + // Find the datapoint with the correct attributes. + if matches!(ty, MetricType::Gauge) { + return gauge + .data_points + .iter() + .any(|datapoint| datapoint.attributes == attributes); + } + } else if let Some(sum) = metric.data.as_any().downcast_ref::>() { + // Note that we can't actually tell if the sum is monotonic or not, so we just check if it's a sum. + if matches!(ty, MetricType::Counter | MetricType::UpDownCounter) { + return sum + .data_points + .iter() + .any(|datapoint| datapoint.attributes == attributes); + } + } else if let Some(histogram) = metric.data.as_any().downcast_ref::>() + { + if matches!(ty, MetricType::Histogram) { + return histogram + .data_points + .iter() + .any(|datapoint| datapoint.attributes == attributes); + } + } + } + false + } + #[allow(dead_code)] pub(crate) fn all(self) -> Vec { self.resource_metrics @@ -935,6 +973,39 @@ macro_rules! assert_histogram_sum { }; } +#[cfg(test)] +macro_rules! assert_histogram_exists { + + ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(result, $name, None, Some($value.into()), &attributes); + }; + + ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(result, $name, None, Some($value.into()), &attributes); + }; + + ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(result, $name, None, Some($value.into()), &attributes); + }; + + ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(result, $name, None, Some($value.into()), &attributes); + }; + + ($name:literal, $value: ty) => { + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &[]); + assert_metric!(result, $name, None, None, &[]); + }; +} + #[cfg(test)] #[allow(unused_macros)] macro_rules! assert_metrics_snapshot { diff --git a/apollo-router/src/plugins/telemetry/config_new/conditions.rs b/apollo-router/src/plugins/telemetry/config_new/conditions.rs index 78418132a7..773b4a4d82 100644 --- a/apollo-router/src/plugins/telemetry/config_new/conditions.rs +++ b/apollo-router/src/plugins/telemetry/config_new/conditions.rs @@ -12,7 +12,7 @@ pub(crate) enum Condition { /// A condition to check a selection against a value. Eq([SelectorOrValue; 2]), /// A condition to check a selection against a selector. - Exist(T), + Exists(T), /// All sub-conditions must be true. All(Vec>), /// At least one sub-conditions must be true. @@ -73,7 +73,7 @@ where } } }, - Condition::Exist(exist) => { + Condition::Exists(exist) => { if exist.on_request(request).is_some() { *self = Condition::True; Some(true) @@ -132,7 +132,7 @@ where let right = eq[1].on_response(response); left == right } - Condition::Exist(exist) => exist.on_response(response).is_some(), + Condition::Exists(exist) => exist.on_response(response).is_some(), Condition::All(all) => all.iter().all(|c| c.evaluate_response(response)), Condition::Any(any) => any.iter().any(|c| c.evaluate_response(response)), Condition::Not(not) => !not.evaluate_response(response), @@ -214,19 +214,20 @@ mod test { fn test_condition_exist() { assert_eq!( None, - Condition::::Exist(TestSelectorReqRes::Req).evaluate_request(&None) + Condition::::Exists(TestSelectorReqRes::Req) + .evaluate_request(&None) ); assert!( - !Condition::::Exist(TestSelectorReqRes::Req) + !Condition::::Exists(TestSelectorReqRes::Req) .evaluate_response(&Some(3i64)) ); assert_eq!( Some(true), - Condition::::Exist(TestSelectorReqRes::Req) + Condition::::Exists(TestSelectorReqRes::Req) .evaluate_request(&Some(2i64)) ); assert!( - Condition::::Exist(TestSelectorReqRes::Resp) + Condition::::Exists(TestSelectorReqRes::Resp) .evaluate_response(&Some(3i64)) ); } diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 222bdefc6a..9c2fbb3347 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -18,6 +18,7 @@ use tokio::time::Instant; use tower::BoxError; use super::attributes::HttpServerAttributes; +use super::DefaultForLevel; use super::Selector; use crate::metrics; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; @@ -56,6 +57,17 @@ pub(crate) struct InstrumentsConfig { } impl InstrumentsConfig { + /// Update the defaults for spans configuration regarding the `default_attribute_requirement_level` + pub(crate) fn update_defaults(&mut self) { + self.router + .attributes + .defaults_for_levels(self.default_attribute_requirement_level); + self.supergraph + .defaults_for_levels(self.default_attribute_requirement_level); + self.subgraph + .defaults_for_levels(self.default_attribute_requirement_level); + } + pub(crate) fn new_router_instruments(&self) -> RouterInstruments { let meter = metrics::meter_provider().meter(METER_NAME); let http_server_request_duration = self @@ -286,6 +298,25 @@ pub(crate) struct RouterInstrumentsConfig { DefaultedStandardInstrument>, } +impl DefaultForLevel for RouterInstrumentsConfig { + fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + match requirement_level { + DefaultAttributeRequirementLevel::Required => { + if !self.http_server_request_duration.is_enabled() { + self.http_server_request_duration = DefaultedStandardInstrument::Bool(true); + } + } + DefaultAttributeRequirementLevel::Recommended => { + // Recommended + if !self.http_server_request_duration.is_enabled() { + self.http_server_request_duration = DefaultedStandardInstrument::Bool(true); + } + } + DefaultAttributeRequirementLevel::None => {} + } + } +} + #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] pub(crate) struct ActiveRequestsAttributes { @@ -352,6 +383,10 @@ where #[serde(deny_unknown_fields, default)] pub(crate) struct SupergraphInstruments {} +impl DefaultForLevel for SupergraphInstruments { + fn defaults_for_level(&mut self, _requirement_level: DefaultAttributeRequirementLevel) {} +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] @@ -372,6 +407,22 @@ pub(crate) struct SubgraphInstrumentsConfig { DefaultedStandardInstrument>, } +impl DefaultForLevel for SubgraphInstrumentsConfig { + fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + match requirement_level { + DefaultAttributeRequirementLevel::Required => { + if !self.http_client_request_duration.is_enabled() { + self.http_client_request_duration = DefaultedStandardInstrument::Bool(true); + } + } + DefaultAttributeRequirementLevel::Recommended => { + // Recommended + } + DefaultAttributeRequirementLevel::None => {} + } + } +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug)] pub(crate) struct Instrument diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 95f7bb6270..bd23cb13e4 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -246,6 +246,7 @@ impl Plugin for Telemetry { let mut config = init.config; config.instrumentation.spans.update_defaults(); + config.instrumentation.instruments.update_defaults(); config.exporters.logging.validate()?; let field_level_instrumentation_ratio = @@ -2117,6 +2118,7 @@ mod tests { "http.response.status_code" = 400, "acme.my_attribute" = "application/json" ); + assert_histogram_exists!("http.server.request.duration", f64); assert_histogram_sum!("acme.request.length", 55.0); let router_req = RouterRequest::fake_builder() @@ -2345,6 +2347,7 @@ mod tests { ])), subgraph.name = "test" ); + assert_histogram_exists!("http.client.request.duration", f64); } .with_metrics() .await; diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml index 2cacb6f29c..5804be4aea 100644 --- a/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml @@ -23,7 +23,7 @@ telemetry: unit: request description: "supergraph requests" condition: - exist: + exists: request_header: "conditional-custom" acme.request.size: # The name of your custom instrument/metric value: @@ -59,7 +59,7 @@ telemetry: "acme.my_attribute": response_header: "content-type" condition: - exist: + exists: request_header: "x-custom" subgraph: acme.subgraph.error_reqs: @@ -72,5 +72,5 @@ telemetry: graphql_error: subgraph_response_errors: "$[*].message" condition: - exist: + exists: subgraph_response_errors: "$[*].message" \ No newline at end of file diff --git a/docs/source/configuration/telemetry/instrumentation/conditions.mdx b/docs/source/configuration/telemetry/instrumentation/conditions.mdx index 4a481a7af0..51ada25ad4 100644 --- a/docs/source/configuration/telemetry/instrumentation/conditions.mdx +++ b/docs/source/configuration/telemetry/instrumentation/conditions.mdx @@ -31,14 +31,14 @@ telemetry: - response_header: x-resp-header ``` -#### `exist` +#### `exists` -The `exist` condition is testing if [selectors](./selectors) is present. +The `exists` condition is testing if [selectors](./selectors) is present. For example, the following condition checks the value of `x-req-header` exists: ```yaml -exist: +exists: - request_header: x-req-header ``` From 3a449f53caef2ef636e32e02559e30b24332d464 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:52:34 +0100 Subject: [PATCH 19/25] update metrics snapshot Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...__test__metrics@telemetry.router.yaml.snap | 8 +-- .../testdata/metrics/telemetry.router.yaml | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap index 063be84f7f..842ddd0340 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__metrics__test__metrics@telemetry.router.yaml.snap @@ -8,11 +8,11 @@ expression: "&metrics.non_zero()" - value: 1 attributes: opt.events: false - opt.instruments: false + opt.instruments: true opt.instruments.default_attribute_requirement_level: false - opt.instruments.router: false - opt.instruments.subgraph: false - opt.instruments.supergraph: false + opt.instruments.router: true + opt.instruments.subgraph: true + opt.instruments.supergraph: true opt.logging.experimental_when_header: true opt.metrics.otlp: true opt.metrics.prometheus: true diff --git a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml index ae206ff7cf..4921115271 100644 --- a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml +++ b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml @@ -44,3 +44,58 @@ telemetry: subgraph: attributes: subgraph.graphql.document: true + instruments: + router: + http.server.request.body.size: + attributes: + # Standard attributes + http.response.status_code: true + "my_attribute": + response_header: "content-type" + http.server.request.duration: + attributes: + # Standard attributes + http.response.status_code: true + http.request.method: true + # Custom attribute + "my_attribute": + response_header: "content-type" + my.request.duration: # The name of your custom instrument/metric + value: duration + type: counter + unit: s + description: "my description" + acme.request.size: # The name of your custom instrument/metric + value: + request_header: "content-length" + type: counter + unit: s + description: "my description" + + acme.request.length: # The name of your custom instrument/metric + value: + request_header: "content-length" + type: histogram + unit: s + description: "my description" + supergraph: + acme.graphql.requests: + value: unit + type: counter + unit: request + description: "supergraph requests" + attributes: + static: hello + graphql_operation_kind: + operation_kind: string + subgraph: + request_including_price1: + value: unit + type: counter + unit: request + description: "supergraph requests" + condition: + exist: + subgraph_response_data: "$.products[*].price1" + attributes: + subgraph.name: true \ No newline at end of file From d9bc51f107ec5796cef909f561cf30878a811fd3 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:21:03 +0100 Subject: [PATCH 20/25] fix tests and docs Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 8 +-- .../testdata/metrics/telemetry.router.yaml | 2 +- .../telemetry/config_new/instruments.rs | 4 ++ .../telemetry/instrumentation/conditions.mdx | 6 +- .../telemetry/instrumentation/instruments.mdx | 59 +++++++++++++------ 5 files changed, 55 insertions(+), 24 deletions(-) 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 75621ffcc3..b36bf1e043 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 @@ -6670,19 +6670,19 @@ expression: "&schema" "attributes": { "type": "object", "properties": { - "http_request_method": { + "http.request.method": { "default": false, "type": "boolean" }, - "server_address": { + "server.address": { "default": false, "type": "boolean" }, - "server_port": { + "server.port": { "default": false, "type": "boolean" }, - "url_scheme": { + "url.scheme": { "default": false, "type": "boolean" } diff --git a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml index 4921115271..b67bf4fc3b 100644 --- a/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml +++ b/apollo-router/src/configuration/testdata/metrics/telemetry.router.yaml @@ -95,7 +95,7 @@ telemetry: unit: request description: "supergraph requests" condition: - exist: + exists: subgraph_response_data: "$.products[*].price1" attributes: subgraph.name: true \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 9c2fbb3347..5447eb92a5 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -320,9 +320,13 @@ impl DefaultForLevel for RouterInstrumentsConfig { #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] pub(crate) struct ActiveRequestsAttributes { + #[serde(rename = "http.request.method")] http_request_method: bool, + #[serde(rename = "server.address")] server_address: bool, + #[serde(rename = "server.port")] server_port: bool, + #[serde(rename = "url.scheme")] url_scheme: bool, } diff --git a/docs/source/configuration/telemetry/instrumentation/conditions.mdx b/docs/source/configuration/telemetry/instrumentation/conditions.mdx index 51ada25ad4..60d3d1fa26 100644 --- a/docs/source/configuration/telemetry/instrumentation/conditions.mdx +++ b/docs/source/configuration/telemetry/instrumentation/conditions.mdx @@ -12,12 +12,16 @@ You can set conditions for when an [instrument](./instruments) should be mutated ## Condition configuration Here is an example of a condition on a custom instrument: -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: router: my.instrument: + value: duration + type: counter + unit: s + description: "my description" # ... # This instrument will only be mutated if the condition evaluates to true condition: diff --git a/docs/source/configuration/telemetry/instrumentation/instruments.mdx b/docs/source/configuration/telemetry/instrumentation/instruments.mdx index 799960a68b..b2dee3766e 100644 --- a/docs/source/configuration/telemetry/instrumentation/instruments.mdx +++ b/docs/source/configuration/telemetry/instrumentation/instruments.mdx @@ -28,7 +28,7 @@ For more information, see the [OpenTelemetry semantic conventions for HTTP metri These standard instruments are configurable in `router.yaml`: -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: @@ -41,7 +41,7 @@ telemetry: Standard instruments can be customized by attaching or removing attributes. -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: @@ -49,11 +49,7 @@ telemetry: router: http.server.active_requests: attributes: - # Standard attributes - http.response.status_code: false - # Custom attribute - "acme.my_attribute": - response_header: "x-my-header" + http.request.method: true ``` See the [attributes](#attributes) configuration for more information. @@ -72,7 +68,7 @@ When defining a custom instrument, make sure to reference the [OpenTelemetry sem In the example configuration below, three custom instruments are defined (`acme.request.duration`, `acme.graphql.requests`, `acme.graphql.subgraph.errors`), one for each service of the router pipeline respectively (`router`, `supergraph`, `subgraph`): -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: @@ -83,6 +79,10 @@ telemetry: type: counter unit: kb description: "my description" + condition: + eq: + - 200 + - response_status: code attributes: http.response.status_code: true "my_attribute": @@ -127,7 +127,7 @@ Valid values: * `required` (default) - required attributes will be attached to standard instruments by default. * `recommended` - recommended attributes will be attached to standard instruments by default. -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: @@ -137,18 +137,29 @@ telemetry: Attributes can be configured individually, so that `required` attributes can be overridden or disabled. For example, `http.response.status_code` is set individually to override the standard value: -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: # Set the default requirement level default_attribute_requirement_level: required router: - http.server.active_requests: + # Standard metrics + http.server.request.body.size: attributes: - # Override attributes for this instrument - network.transport: true + # Standard attributes http.response.status_code: false + # Custom attribute + "acme.my_attribute": + response_header: "x-my-header" + # Standard metrics + http.server.active_requests: + attributes: + # Standard attributes, different than other ones provides in standard metrics, custom attributes are not available on this standard metric + http.request.method: false + server.address: true + server.port: true + url.scheme: true ``` @@ -165,7 +176,7 @@ The `router`, `supergraph` and `subgraph` sections are used to define custom ins To define a custom instrument, add a new key to `router.yaml` as `telemetry.instruments..`. For example, add a custom instrument `acme.request.duration`: -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: @@ -278,32 +289,44 @@ telemetry: condition: eq: - 200 - - response_header: Status + - response_status: code ``` #### `attributes` Instruments may have attributes attached to them from the router pipeline. These attributes are used to filter and group metrics in your APM. -Attributes may be drawn from [standard attributes](./standard-attributes) or [selectors](./selectors). +Attributes may be drawn from [standard attributes](./standard-attributes) or [selectors](./selectors) except for the standard metric `http.server.active_requests`. The attributes available depend on the service of the pipeline. -```yaml title="future.router.yaml" +```yaml title="router.yaml" telemetry: instrumentation: instruments: router: # Standard metrics - http.server.active_requests: + http.server.request.body.size: attributes: # Standard attributes http.response.status_code: false # Custom attribute "acme.my_attribute": response_header: "x-my-header" + # Standard metrics + http.server.active_requests: + attributes: + # Standard attributes, different than other ones provides in standard metrics, custom attributes are not available on this standard metric + http.request.method: false + server.address: true + server.port: true + url.scheme: true # Custom metric acme.metric: + value: duration + type: counter + unit: s + description: "my description" attributes: http.response.status_code: true "my_attribute": From c79f6be2c538fc1abe1aeaa1326d77b22bc1da7e Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:22:22 +0100 Subject: [PATCH 21/25] changelog Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- .changesets/feat_bnjjj_feat_4319.md | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .changesets/feat_bnjjj_feat_4319.md diff --git a/.changesets/feat_bnjjj_feat_4319.md b/.changesets/feat_bnjjj_feat_4319.md new file mode 100644 index 0000000000..ff9f543046 --- /dev/null +++ b/.changesets/feat_bnjjj_feat_4319.md @@ -0,0 +1,40 @@ +### Add support of instruments in configuration for telemetry ([Issue #4319](https://github.com/apollographql/router/issues/4319)) + +Add support for custom and standard instruments through the configuration file. You'll be able to add your own custom metrics just using the configuration file. It can be conditional, get values from selectors like headers, context, body. And the metrics can have different types like `histogram` or `counter`. + +Example: + +```yaml title="router.yaml" +telemetry: + instrumentation: + instruments: + router: + http.server.active_requests: true + acme.request.duration: + value: duration + type: counter + unit: kb + description: "my description" + attributes: + http.response.status_code: true + "my_attribute": + response_header: "x-my-header" + + supergraph: + acme.graphql.requests: + value: unit + type: counter + unit: count + description: "supergraph requests" + + subgraph: + acme.graphql.subgraph.errors: + value: unit + type: counter + unit: count + description: "my description" +``` + +[Documentation](https://www.apollographql.com/docs/router/configuration/telemetry/instrumentation/instruments) + +By [@bnjjj](https://github.com/bnjjj) in https://github.com/apollographql/router/pull/4771 \ No newline at end of file From 1b4f3699a7b07e75e7c47a17688f6d54cf75b279 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:19:50 +0100 Subject: [PATCH 22/25] fix requirement_level configuration Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 4 +- apollo-router/src/metrics/mod.rs | 8 +- .../telemetry/config_new/attributes.rs | 191 ++++++++---------- .../telemetry/config_new/extendable.rs | 9 +- .../telemetry/config_new/instruments.rs | 114 ++++++++--- .../src/plugins/telemetry/config_new/mod.rs | 19 +- .../src/plugins/telemetry/config_new/spans.rs | 89 ++++++-- apollo-router/src/plugins/telemetry/mod.rs | 90 +++++++++ apollo-router/src/plugins/telemetry/otlp.rs | 1 + .../testdata/custom_instruments.router.yaml | 2 +- .../custom_instruments_level.router.yaml | 76 +++++++ .../telemetry/instrumentation/instruments.mdx | 12 +- 12 files changed, 436 insertions(+), 179 deletions(-) create mode 100644 apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml 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 26444c9b2e..f6bcc6bc47 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 @@ -6677,8 +6677,8 @@ expression: "&schema" "description": "Instrument configuration", "type": "object", "properties": { - "default_attribute_requirement_level": { - "description": "The attributes to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation.", + "default_requirement_level": { + "description": "The attributes and instruments to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation.", "oneOf": [ { "description": "No default attributes set on spans, you have to set it one by one in the configuration to enable some attributes", diff --git a/apollo-router/src/metrics/mod.rs b/apollo-router/src/metrics/mod.rs index 23a914c11f..c184cf183b 100644 --- a/apollo-router/src/metrics/mod.rs +++ b/apollo-router/src/metrics/mod.rs @@ -979,25 +979,25 @@ macro_rules! assert_histogram_exists { ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); - assert_metric!(result, $name, None, Some($value.into()), &attributes); + assert_metric!(result, $name, None, None, &attributes); }; ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); - assert_metric!(result, $name, None, Some($value.into()), &attributes); + assert_metric!(result, $name, None, None, &attributes); }; ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); - assert_metric!(result, $name, None, Some($value.into()), &attributes); + assert_metric!(result, $name, None, None, &attributes); }; ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); - assert_metric!(result, $name, None, Some($value.into()), &attributes); + assert_metric!(result, $name, None, None, &attributes); }; ($name:literal, $value: ty) => { diff --git a/apollo-router/src/plugins/telemetry/config_new/attributes.rs b/apollo-router/src/plugins/telemetry/config_new/attributes.rs index 5a9bd2a7a9..81f99cc8cd 100644 --- a/apollo-router/src/plugins/telemetry/config_new/attributes.rs +++ b/apollo-router/src/plugins/telemetry/config_new/attributes.rs @@ -44,6 +44,7 @@ use crate::plugins::telemetry::config_new::trace_id; use crate::plugins::telemetry::config_new::DatadogId; use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::config_new::Selectors; +use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::services::router; use crate::services::router::Request; use crate::services::subgraph; @@ -101,9 +102,13 @@ pub(crate) struct RouterAttributes { } impl DefaultForLevel for RouterAttributes { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - self.common.defaults_for_level(requirement_level); - self.server.defaults_for_level(requirement_level); + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.common.defaults_for_level(requirement_level, kind); + self.server.defaults_for_level(requirement_level, kind); } } @@ -134,7 +139,11 @@ pub(crate) struct SupergraphAttributes { } impl DefaultForLevel for SupergraphAttributes { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + _kind: TelemetryDataKind, + ) { match requirement_level { DefaultAttributeRequirementLevel::Required => {} DefaultAttributeRequirementLevel::Recommended => { @@ -185,7 +194,11 @@ pub(crate) struct SubgraphAttributes { } impl DefaultForLevel for SubgraphAttributes { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + _kind: TelemetryDataKind, + ) { match requirement_level { DefaultAttributeRequirementLevel::Required => { if self.subgraph_name.is_none() { @@ -300,7 +313,11 @@ pub(crate) struct HttpCommonAttributes { } impl DefaultForLevel for HttpCommonAttributes { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { match requirement_level { DefaultAttributeRequirementLevel::Required => { if self.error_type.is_none() { @@ -315,17 +332,26 @@ impl DefaultForLevel for HttpCommonAttributes { } DefaultAttributeRequirementLevel::Recommended => { // Recommended - if self.http_request_body_size.is_none() { - self.http_request_body_size = Some(true); - } - if self.http_response_body_size.is_none() { - self.http_response_body_size = Some(true); - } - if self.network_protocol_version.is_none() { - self.network_protocol_version = Some(true); - } - if self.network_type.is_none() { - self.network_type = Some(true); + match kind { + TelemetryDataKind::Traces => { + if self.http_request_body_size.is_none() { + self.http_request_body_size = Some(true); + } + if self.http_response_body_size.is_none() { + self.http_response_body_size = Some(true); + } + if self.network_protocol_version.is_none() { + self.network_protocol_version = Some(true); + } + if self.network_type.is_none() { + self.network_type = Some(true); + } + } + TelemetryDataKind::Metrics => { + if self.network_protocol_version.is_none() { + self.network_protocol_version = Some(true); + } + } } } DefaultAttributeRequirementLevel::None => {} @@ -429,104 +455,59 @@ pub(crate) struct HttpServerAttributes { } impl DefaultForLevel for HttpServerAttributes { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { match requirement_level { - DefaultAttributeRequirementLevel::Required => { - if self.url_scheme.is_none() { - self.url_scheme = Some(true); - } - if self.url_path.is_none() { - self.url_path = Some(true); - } - if self.url_query.is_none() { - self.url_query = Some(true); - } + DefaultAttributeRequirementLevel::Required => match kind { + TelemetryDataKind::Traces => { + if self.url_scheme.is_none() { + self.url_scheme = Some(true); + } + if self.url_path.is_none() { + self.url_path = Some(true); + } + if self.url_query.is_none() { + self.url_query = Some(true); + } - if self.http_route.is_none() { - self.http_route = Some(true); - } - } - DefaultAttributeRequirementLevel::Recommended => { - if self.client_address.is_none() { - self.client_address = Some(true); - } - if self.server_address.is_none() { - self.server_address = Some(true); + if self.http_route.is_none() { + self.http_route = Some(true); + } } - if self.server_port.is_none() && self.server_address.is_some() { - self.server_port = Some(true); + TelemetryDataKind::Metrics => { + if self.server_address.is_none() { + self.server_address = Some(true); + } + if self.server_port.is_none() && self.server_address.is_some() { + self.server_port = Some(true); + } } - if self.user_agent_original.is_none() { - self.user_agent_original = Some(true); + }, + DefaultAttributeRequirementLevel::Recommended => match kind { + TelemetryDataKind::Traces => { + if self.client_address.is_none() { + self.client_address = Some(true); + } + if self.server_address.is_none() { + self.server_address = Some(true); + } + if self.server_port.is_none() && self.server_address.is_some() { + self.server_port = Some(true); + } + if self.user_agent_original.is_none() { + self.user_agent_original = Some(true); + } } - } + TelemetryDataKind::Metrics => {} + }, DefaultAttributeRequirementLevel::None => {} } } } -/// Attributes for HTTP clients -/// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client -#[derive(Deserialize, JsonSchema, Clone, Default, Debug)] -#[serde(deny_unknown_fields, default)] -pub(crate) struct HttpClientAttributes { - /// The ordinal number of request resending attempt. - /// Examples: - /// * - /// Requirement level: Recommended: if and only if request was retried. - #[serde(rename = "http.resend_count")] - http_resend_count: Option, - - /// Peer address of the network connection - IP address or Unix domain socket name. - /// Examples: - /// * 10.1.2.80 - /// * /tmp/my.sock - /// Requirement level: Recommended: If different than server.address. - #[serde(rename = "network.peer.address")] - network_peer_address: Option, - - /// Peer port number of the network connection. - /// Examples: - /// * 65123 - /// Requirement level: Recommended: If network.peer.address is set. - #[serde(rename = "network.peer.port")] - network_peer_port: Option, - - /// Host identifier of the “URI origin” HTTP request is sent to. - /// Examples: - /// * example.com - /// * 10.1.2.80 - /// * /tmp/my.sock - /// Requirement level: Required - #[serde(rename = "server.address")] - server_address: Option, - - /// Port identifier of the “URI origin” HTTP request is sent to. - /// Examples: - /// * 80 - /// * 8080 - /// * 433 - /// Requirement level: Conditionally Required - #[serde(rename = "server.port")] - server_port: Option, - - /// Absolute URL describing a network resource according to RFC3986 - /// Examples: - /// * https://www.foo.bar/search?q=OpenTelemetry#SemConv; - /// * localhost - /// Requirement level: Required - #[serde(rename = "url.full")] - url_full: Option, - - /// Value of the HTTP User-Agent header sent by the client. - /// Examples: - /// * CERN-LineMode/2.15 - /// * libwww/2.17b3 - /// Requirement level: Opt-In - #[serde(rename = "user_agent.original")] - user_agent_original: Option, -} - impl Selectors for RouterAttributes { type Request = router::Request; type Response = router::Response; diff --git a/apollo-router/src/plugins/telemetry/config_new/extendable.rs b/apollo-router/src/plugins/telemetry/config_new/extendable.rs index f8a81daa39..ae08b71539 100644 --- a/apollo-router/src/plugins/telemetry/config_new/extendable.rs +++ b/apollo-router/src/plugins/telemetry/config_new/extendable.rs @@ -22,6 +22,7 @@ use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequireme use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::config_new::Selector; use crate::plugins::telemetry::config_new::Selectors; +use crate::plugins::telemetry::otlp::TelemetryDataKind; /// This struct can be used as an attributes container, it has a custom JsonSchema implementation that will merge the schemas of the attributes and custom fields. #[derive(Clone, Debug)] @@ -38,8 +39,12 @@ impl DefaultForLevel for Extendable where Att: DefaultForLevel + Default, { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - self.attributes.defaults_for_level(requirement_level); + 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/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index 5447eb92a5..c94993ea27 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -31,6 +31,7 @@ use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::plugins::telemetry::config_new::Selectors; +use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; @@ -42,8 +43,8 @@ const METER_NAME: &str = "apollo/router"; #[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, default)] pub(crate) struct InstrumentsConfig { - /// The attributes to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation. - pub(crate) default_attribute_requirement_level: DefaultAttributeRequirementLevel, + /// The attributes and instruments to include by default in instruments based on their level as specified in the otel semantic conventions and Apollo documentation. + pub(crate) default_requirement_level: DefaultAttributeRequirementLevel, /// Router service instruments. For more information see documentation on Router lifecycle. pub(crate) router: @@ -61,11 +62,11 @@ impl InstrumentsConfig { pub(crate) fn update_defaults(&mut self) { self.router .attributes - .defaults_for_levels(self.default_attribute_requirement_level); + .defaults_for_levels(self.default_requirement_level, TelemetryDataKind::Metrics); self.supergraph - .defaults_for_levels(self.default_attribute_requirement_level); + .defaults_for_levels(self.default_requirement_level, TelemetryDataKind::Metrics); self.subgraph - .defaults_for_levels(self.default_attribute_requirement_level); + .defaults_for_levels(self.default_requirement_level, TelemetryDataKind::Metrics); } pub(crate) fn new_router_instruments(&self) -> RouterInstruments { @@ -299,21 +300,19 @@ pub(crate) struct RouterInstrumentsConfig { } impl DefaultForLevel for RouterInstrumentsConfig { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - match requirement_level { - DefaultAttributeRequirementLevel::Required => { - if !self.http_server_request_duration.is_enabled() { - self.http_server_request_duration = DefaultedStandardInstrument::Bool(true); - } - } - DefaultAttributeRequirementLevel::Recommended => { - // Recommended - if !self.http_server_request_duration.is_enabled() { - self.http_server_request_duration = DefaultedStandardInstrument::Bool(true); - } - } - DefaultAttributeRequirementLevel::None => {} - } + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.http_server_request_duration + .defaults_for_levels(requirement_level, kind); + self.http_server_active_requests + .defaults_for_levels(requirement_level, kind); + self.http_server_request_body_size + .defaults_for_levels(requirement_level, kind); + self.http_server_response_body_size + .defaults_for_levels(requirement_level, kind); } } @@ -330,6 +329,23 @@ pub(crate) struct ActiveRequestsAttributes { url_scheme: bool, } +impl DefaultForLevel for ActiveRequestsAttributes { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + _kind: TelemetryDataKind, + ) { + match requirement_level { + DefaultAttributeRequirementLevel::Required => { + self.http_request_method = true; + self.url_scheme = true; + } + DefaultAttributeRequirementLevel::Recommended + | DefaultAttributeRequirementLevel::None => {} + } + } +} + #[allow(dead_code)] #[derive(Clone, Deserialize, JsonSchema, Debug)] #[serde(deny_unknown_fields, untagged)] @@ -353,6 +369,34 @@ impl DefaultedStandardInstrument { } } +impl DefaultForLevel for DefaultedStandardInstrument +where + T: DefaultForLevel + Clone + Default, +{ + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + match self { + DefaultedStandardInstrument::Bool(_enabled) => match requirement_level { + DefaultAttributeRequirementLevel::None => {} + DefaultAttributeRequirementLevel::Required + | DefaultAttributeRequirementLevel::Recommended => { + let mut attrs = T::default(); + attrs.defaults_for_levels(requirement_level, kind); + *self = Self::Extendable { + attributes: Arc::new(attrs), + } + } + }, + DefaultedStandardInstrument::Extendable { attributes } => { + Arc::make_mut(attributes).defaults_for_levels(requirement_level, kind); + } + } + } +} + impl Selectors for DefaultedStandardInstrument where T: Selectors, @@ -388,7 +432,12 @@ where pub(crate) struct SupergraphInstruments {} impl DefaultForLevel for SupergraphInstruments { - fn defaults_for_level(&mut self, _requirement_level: DefaultAttributeRequirementLevel) {} + fn defaults_for_level( + &mut self, + _requirement_level: DefaultAttributeRequirementLevel, + _kind: TelemetryDataKind, + ) { + } } #[allow(dead_code)] @@ -412,18 +461,17 @@ pub(crate) struct SubgraphInstrumentsConfig { } impl DefaultForLevel for SubgraphInstrumentsConfig { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - match requirement_level { - DefaultAttributeRequirementLevel::Required => { - if !self.http_client_request_duration.is_enabled() { - self.http_client_request_duration = DefaultedStandardInstrument::Bool(true); - } - } - DefaultAttributeRequirementLevel::Recommended => { - // Recommended - } - DefaultAttributeRequirementLevel::None => {} - } + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.http_client_request_duration + .defaults_for_level(requirement_level, kind); + self.http_client_request_body_size + .defaults_for_level(requirement_level, kind); + self.http_client_response_body_size + .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 1449b326cc..67d758456d 100644 --- a/apollo-router/src/plugins/telemetry/config_new/mod.rs +++ b/apollo-router/src/plugins/telemetry/config_new/mod.rs @@ -7,6 +7,7 @@ use tower::BoxError; use tracing::Span; use tracing_opentelemetry::OpenTelemetrySpanExt; +use super::otlp::TelemetryDataKind; use crate::plugins::telemetry::config::AttributeValue; use crate::plugins::telemetry::config_new::attributes::DefaultAttributeRequirementLevel; @@ -40,16 +41,24 @@ pub(crate) trait Selector { pub(crate) trait DefaultForLevel { /// Don't call this directly, use `defaults_for_levels` instead. - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel); - fn defaults_for_levels(&mut self, requirement_level: DefaultAttributeRequirementLevel) { + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ); + fn defaults_for_levels( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { match requirement_level { DefaultAttributeRequirementLevel::None => {} DefaultAttributeRequirementLevel::Required => { - self.defaults_for_level(DefaultAttributeRequirementLevel::Required) + self.defaults_for_level(DefaultAttributeRequirementLevel::Required, kind) } DefaultAttributeRequirementLevel::Recommended => { - self.defaults_for_level(DefaultAttributeRequirementLevel::Required); - self.defaults_for_level(DefaultAttributeRequirementLevel::Recommended); + self.defaults_for_level(DefaultAttributeRequirementLevel::Required, kind); + self.defaults_for_level(DefaultAttributeRequirementLevel::Recommended, kind); } } } diff --git a/apollo-router/src/plugins/telemetry/config_new/spans.rs b/apollo-router/src/plugins/telemetry/config_new/spans.rs index ed1f139e07..396077e7a1 100644 --- a/apollo-router/src/plugins/telemetry/config_new/spans.rs +++ b/apollo-router/src/plugins/telemetry/config_new/spans.rs @@ -10,6 +10,7 @@ use crate::plugins::telemetry::config_new::selectors::RouterSelector; use crate::plugins::telemetry::config_new::selectors::SubgraphSelector; use crate::plugins::telemetry::config_new::selectors::SupergraphSelector; use crate::plugins::telemetry::config_new::DefaultForLevel; +use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::plugins::telemetry::span_factory::SpanMode; #[derive(Deserialize, JsonSchema, Clone, Default, Debug)] @@ -38,12 +39,18 @@ pub(crate) struct Spans { impl Spans { /// Update the defaults for spans configuration regarding the `default_attribute_requirement_level` pub(crate) fn update_defaults(&mut self) { - self.router - .defaults_for_levels(self.default_attribute_requirement_level); - self.supergraph - .defaults_for_levels(self.default_attribute_requirement_level); - self.subgraph - .defaults_for_levels(self.default_attribute_requirement_level); + self.router.defaults_for_levels( + self.default_attribute_requirement_level, + TelemetryDataKind::Traces, + ); + self.supergraph.defaults_for_levels( + self.default_attribute_requirement_level, + TelemetryDataKind::Traces, + ); + self.subgraph.defaults_for_levels( + self.default_attribute_requirement_level, + TelemetryDataKind::Traces, + ); } } @@ -55,8 +62,12 @@ pub(crate) struct RouterSpans { } impl DefaultForLevel for RouterSpans { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - self.attributes.defaults_for_level(requirement_level); + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.attributes.defaults_for_level(requirement_level, kind); } } @@ -67,8 +78,12 @@ pub(crate) struct SupergraphSpans { pub(crate) attributes: Extendable, } impl DefaultForLevel for SupergraphSpans { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - self.attributes.defaults_for_level(requirement_level); + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.attributes.defaults_for_level(requirement_level, kind); } } @@ -80,8 +95,12 @@ pub(crate) struct SubgraphSpans { } impl DefaultForLevel for SubgraphSpans { - fn defaults_for_level(&mut self, requirement_level: DefaultAttributeRequirementLevel) { - self.attributes.defaults_for_level(requirement_level); + fn defaults_for_level( + &mut self, + requirement_level: DefaultAttributeRequirementLevel, + kind: TelemetryDataKind, + ) { + self.attributes.defaults_for_level(requirement_level, kind); } } @@ -105,6 +124,7 @@ mod test { use crate::plugins::telemetry::config_new::spans::SupergraphSpans; use crate::plugins::telemetry::config_new::DefaultForLevel; use crate::plugins::telemetry::config_new::Selectors; + use crate::plugins::telemetry::otlp::TelemetryDataKind; use crate::services::router; use crate::services::subgraph; use crate::services::supergraph; @@ -112,7 +132,10 @@ mod test { #[test] fn test_router_spans_level_none() { let mut spans = RouterSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::None); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::None, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &router::Request::fake_builder() .method(http::Method::POST) @@ -135,7 +158,10 @@ mod test { #[test] fn test_router_spans_level_required() { let mut spans = RouterSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Required); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Required, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &router::Request::fake_builder() .method(http::Method::POST) @@ -158,7 +184,10 @@ mod test { #[test] fn test_router_spans_level_recommended() { let mut spans = RouterSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Recommended); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Recommended, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &router::Request::fake_builder() .method(http::Method::POST) @@ -181,7 +210,10 @@ mod test { #[test] fn test_supergraph_spans_level_none() { let mut spans = SupergraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::None); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::None, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &supergraph::Request::fake_builder() .query("query { __typename }") @@ -194,7 +226,10 @@ mod test { #[test] fn test_supergraph_spans_level_required() { let mut spans = SupergraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Required); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Required, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &supergraph::Request::fake_builder() .query("query { __typename }") @@ -207,7 +242,10 @@ mod test { #[test] fn test_supergraph_spans_level_recommended() { let mut spans = SupergraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Recommended); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Recommended, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &supergraph::Request::fake_builder() .query("query { __typename }") @@ -220,7 +258,10 @@ mod test { #[test] fn test_subgraph_spans_level_none() { let mut spans = SubgraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::None); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::None, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &subgraph::Request::fake_builder() .subgraph_request( @@ -241,7 +282,10 @@ mod test { #[test] fn test_subgraph_spans_level_required() { let mut spans = SubgraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Required); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Required, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &subgraph::Request::fake_builder() .subgraph_request( @@ -262,7 +306,10 @@ mod test { #[test] fn test_subgraph_spans_level_recommended() { let mut spans = SubgraphSpans::default(); - spans.defaults_for_levels(DefaultAttributeRequirementLevel::Recommended); + spans.defaults_for_levels( + DefaultAttributeRequirementLevel::Recommended, + TelemetryDataKind::Traces, + ); let values = spans.attributes.on_request( &subgraph::Request::fake_builder() .subgraph_request( diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index 488f017596..c1012e8e42 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -2147,6 +2147,96 @@ mod tests { .await; } + #[tokio::test] + async fn test_custom_router_instruments_with_requirement_level() { + async { + let plugin = create_plugin_with_config(include_str!( + "testdata/custom_instruments_level.router.yaml" + )) + .await; + + let mut mock_bad_request_service = MockRouterService::new(); + mock_bad_request_service + .expect_call() + .times(2) + .returning(move |req: RouterRequest| { + Ok(RouterResponse::fake_builder() + .context(req.context) + .status_code(StatusCode::BAD_REQUEST) + .header("content-type", "application/json") + .data(json!({"errors": [{"message": "nope"}]})) + .build() + .unwrap()) + }); + let mut bad_request_router_service = + plugin.router_service(BoxService::new(mock_bad_request_service)); + let router_req = RouterRequest::fake_builder() + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql"); + let _router_response = bad_request_router_service + .ready() + .await + .unwrap() + .call(router_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + + assert_counter!("acme.graphql.custom_req", 1.0); + assert_histogram_sum!( + "http.server.request.body.size", + 55.0, + "acme.my_attribute" = "application/json", + "error.type" = "Bad Request", + "http.response.status_code" = 400, + "network.protocol.version" = "HTTP/1.1" + ); + assert_histogram_exists!( + "http.server.request.duration", + f64, + "error.type" = "Bad Request", + "http.response.status_code" = 400, + "network.protocol.version" = "HTTP/1.1", + "http.request.method" = "GET" + ); + assert_histogram_sum!("acme.request.length", 55.0); + + let router_req = RouterRequest::fake_builder() + .header("x-custom", "TEST") + .header("custom-length", "5") + .header("content-length", "5") + .header("content-type", "application/graphql"); + let _router_response = bad_request_router_service + .ready() + .await + .unwrap() + .call(router_req.build().unwrap()) + .await + .unwrap() + .next_response() + .await + .unwrap(); + assert_counter!("acme.graphql.custom_req", 1.0); + assert_histogram_sum!("acme.request.length", 60.0); + assert_histogram_sum!( + "http.server.request.body.size", + 60.0, + "http.response.status_code" = 400, + "acme.my_attribute" = "application/json", + "error.type" = "Bad Request", + "http.response.status_code" = 400, + "network.protocol.version" = "HTTP/1.1" + ); + } + .with_metrics() + .await; + } + #[tokio::test] async fn test_custom_supergraph_instruments() { async { diff --git a/apollo-router/src/plugins/telemetry/otlp.rs b/apollo-router/src/plugins/telemetry/otlp.rs index 2545a148a1..e1f4dfe966 100644 --- a/apollo-router/src/plugins/telemetry/otlp.rs +++ b/apollo-router/src/plugins/telemetry/otlp.rs @@ -65,6 +65,7 @@ pub(crate) struct Config { pub(crate) temporality: Temporality, } +#[derive(Copy, Clone)] pub(crate) enum TelemetryDataKind { Traces, Metrics, diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml index 5804be4aea..b32c4bfa44 100644 --- a/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/custom_instruments.router.yaml @@ -4,7 +4,7 @@ telemetry: client_version_header: version_header instrumentation: instruments: - default_attribute_requirement_level: none + default_requirement_level: none router: http.server.request.body.size: attributes: diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml new file mode 100644 index 0000000000..c4d80f0312 --- /dev/null +++ b/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml @@ -0,0 +1,76 @@ +telemetry: + apollo: + client_name_header: name_header + client_version_header: version_header + instrumentation: + instruments: + default_requirement_level: recommended + router: + http.server.request.body.size: + attributes: + # Standard attributes + http.request.method: false + "acme.my_attribute": + response_header: "content-type" + acme.request.duration: # The name of your custom instrument/metric + value: duration + type: counter + unit: s + description: "my description" + acme.graphql.custom_req: + value: unit + type: counter + unit: request + description: "supergraph requests" + condition: + exists: + request_header: "conditional-custom" + acme.request.size: # The name of your custom instrument/metric + value: + request_header: "custom-length" + type: counter + unit: s + condition: + all: + - eq: + - request_header: "x-custom" + - "TEST" + - eq: + - response_header: "content-type" + - "application/graphql" + description: "my description" + + acme.request.length: # The name of your custom instrument/metric + value: + request_header: "custom-length" + type: histogram + unit: s + description: "my description" + supergraph: + acme.graphql.requests: + value: unit + type: counter + unit: request + description: "supergraph requests" + attributes: + graphql.document: true + graphql_query: + query: string + "acme.my_attribute": + response_header: "content-type" + condition: + exists: + request_header: "x-custom" + subgraph: + acme.subgraph.error_reqs: + value: unit + type: counter + unit: request + description: "subgraph requests in error" + attributes: + subgraph.name: true + graphql_error: + subgraph_response_errors: "$[*].message" + condition: + exists: + subgraph_response_errors: "$[*].message" \ No newline at end of file diff --git a/docs/source/configuration/telemetry/instrumentation/instruments.mdx b/docs/source/configuration/telemetry/instrumentation/instruments.mdx index b2dee3766e..cff1cfd7ff 100644 --- a/docs/source/configuration/telemetry/instrumentation/instruments.mdx +++ b/docs/source/configuration/telemetry/instrumentation/instruments.mdx @@ -45,7 +45,7 @@ Standard instruments can be customized by attaching or removing attributes. telemetry: instrumentation: instruments: - default_attribute_requirement_level: required + default_requirement_level: required router: http.server.active_requests: attributes: @@ -118,9 +118,9 @@ Some particular guidelines to note: ### Instrument configuration -#### `default_attribute_requirement_level` +#### `default_requirement_level` -The `default_attribute_requirement_level` option sets the default attributes to attach to standard instruments, as defined by [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/otel/common/attribute-requirement-level/). +The `default_requirement_level` option sets the default attributes to attach to default standard instruments, as defined by [OpenTelemetry semantic conventions](https://opentelemetry.io/docs/specs/otel/common/attribute-requirement-level/). Valid values: @@ -132,7 +132,7 @@ telemetry: instrumentation: instruments: # Set the default requirement level - default_attribute_requirement_level: required #highlight-line + default_requirement_level: required #highlight-line ``` Attributes can be configured individually, so that `required` attributes can be overridden or disabled. For example, `http.response.status_code` is set individually to override the standard value: @@ -142,7 +142,7 @@ telemetry: instrumentation: instruments: # Set the default requirement level - default_attribute_requirement_level: required + default_requirement_level: required router: # Standard metrics http.server.request.body.size: @@ -342,7 +342,7 @@ telemetry: | `` | | | The name of the custom instrument. | | `attributes` | [standard attributes](./standard-attributes) or [selectors](./selectors) | | The attributes of the custom instrument. | | `condition` | [conditions](./conditions) | | The a condition for mutating the instrument. | -| `default_attribute_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | +| `default_requirement_level` | `required`\|`recommended` | `required` | The default attribute requirement level. | | `type` | `counter`\|`histogram` | | The name of the custom instrument. | | `unit` | | | A unit name, for example `By` or `{request}`.| | `description` | | | The description of the custom instrument. | From 3b0559f657b844ed353a2904ec04d5c3119acb86 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 29 Mar 2024 11:57:46 +0100 Subject: [PATCH 23/25] fix tests Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- apollo-router/src/metrics/mod.rs | 33 ++ .../telemetry/config_new/instruments.rs | 284 ++++++++++-------- apollo-router/src/plugins/telemetry/mod.rs | 104 ++++++- .../custom_instruments_level.router.yaml | 1 + 4 files changed, 287 insertions(+), 135 deletions(-) diff --git a/apollo-router/src/metrics/mod.rs b/apollo-router/src/metrics/mod.rs index c184cf183b..db3487f7d1 100644 --- a/apollo-router/src/metrics/mod.rs +++ b/apollo-router/src/metrics/mod.rs @@ -1006,6 +1006,39 @@ macro_rules! assert_histogram_exists { }; } +#[cfg(test)] +macro_rules! assert_histogram_not_exists { + + ($($name:ident).+, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(!result, $name, None, None, &attributes); + }; + + ($($name:ident).+, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>(stringify!($($name).+), crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(!result, $name, None, None, &attributes); + }; + + ($name:literal, $value: ty, $($attr_key:literal = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new($attr_key, $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(!result, $name, None, None, &attributes); + }; + + ($name:literal, $value: ty, $($($attr_key:ident).+ = $attr_value:expr),+) => { + let attributes = vec![$(opentelemetry::KeyValue::new(stringify!($($attr_key).+), $attr_value)),+]; + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &attributes); + assert_metric!(!result, $name, None, None, &attributes); + }; + + ($name:literal, $value: ty) => { + let result = crate::metrics::collect_metrics().metric_exists::<$value>($name, crate::metrics::test_utils::MetricType::Histogram, &[]); + assert_metric!(!result, $name, None, None, &[]); + }; +} + #[cfg(test)] #[allow(unused_macros)] macro_rules! assert_metrics_snapshot { diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index c94993ea27..76fc854b13 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -83,74 +83,77 @@ impl InstrumentsConfig { attributes: Vec::new(), selector: None, selectors: match &self.router.attributes.http_server_request_duration { - DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => None, DefaultedStandardInstrument::Extendable { attributes } => { Some(attributes.clone()) } }, }), }); - let http_server_request_body_size = self - .router - .attributes - .http_server_request_body_size - .is_enabled() - .then(|| { - let mut nb_attributes = 0; - let selectors = match &self.router.attributes.http_server_request_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - nb_attributes = attributes.custom.len(); - Some(attributes.clone()) - } - }; - CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some( - meter.f64_histogram("http.server.request.body.size").init(), - ), - attributes: Vec::with_capacity(nb_attributes), - selector: Some(Arc::new(RouterSelector::RequestHeader { - request_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors, - }), - } - }); - let http_server_response_body_size = self - .router - .attributes - .http_server_response_body_size - .is_enabled() - .then(|| { - let mut nb_attributes = 0; - let selectors = match &self.router.attributes.http_server_response_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - nb_attributes = attributes.custom.len(); - Some(attributes.clone()) + let http_server_request_body_size = + self.router + .attributes + .http_server_request_body_size + .is_enabled() + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.router.attributes.http_server_request_body_size { + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.server.request.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(RouterSelector::RequestHeader { + request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), } - }; + }); + let http_server_response_body_size = + self.router + .attributes + .http_server_response_body_size + .is_enabled() + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.router.attributes.http_server_response_body_size { + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; - CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some( - meter.f64_histogram("http.server.response.body.size").init(), - ), - attributes: Vec::with_capacity(nb_attributes), - selector: Some(Arc::new(RouterSelector::ResponseHeader { - response_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors, - }), - } - }); + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.server.response.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(RouterSelector::ResponseHeader { + response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), + } + }); let http_server_active_requests = self .router .attributes @@ -164,7 +167,8 @@ impl InstrumentsConfig { .init(), ), attrs_config: match &self.router.attributes.http_server_active_requests { - DefaultedStandardInstrument::Bool(_) => Default::default(), + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => Default::default(), DefaultedStandardInstrument::Extendable { attributes } => { attributes.clone() } @@ -191,7 +195,9 @@ impl InstrumentsConfig { .then(|| { let mut nb_attributes = 0; let selectors = match &self.subgraph.attributes.http_client_request_duration { - DefaultedStandardInstrument::Bool(_) => None, + DefaultedStandardInstrument::Bool(_) | DefaultedStandardInstrument::Unset => { + None + } DefaultedStandardInstrument::Extendable { attributes } => { nb_attributes = attributes.custom.len(); Some(attributes.clone()) @@ -207,66 +213,68 @@ impl InstrumentsConfig { }), } }); - let http_client_request_body_size = self - .subgraph - .attributes - .http_client_request_body_size - .is_enabled() - .then(|| { - let mut nb_attributes = 0; - let selectors = match &self.subgraph.attributes.http_client_request_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - nb_attributes = attributes.custom.len(); - Some(attributes.clone()) + let http_client_request_body_size = + self.subgraph + .attributes + .http_client_request_body_size + .is_enabled() + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.subgraph.attributes.http_client_request_body_size { + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.client.request.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(SubgraphSelector::SubgraphRequestHeader { + subgraph_request_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), } - }; - CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some( - meter.f64_histogram("http.client.request.body.size").init(), - ), - attributes: Vec::with_capacity(nb_attributes), - selector: Some(Arc::new(SubgraphSelector::SubgraphRequestHeader { - subgraph_request_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors, - }), - } - }); - let http_client_response_body_size = self - .subgraph - .attributes - .http_client_response_body_size - .is_enabled() - .then(|| { - let mut nb_attributes = 0; - let selectors = match &self.subgraph.attributes.http_client_response_body_size { - DefaultedStandardInstrument::Bool(_) => None, - DefaultedStandardInstrument::Extendable { attributes } => { - nb_attributes = attributes.custom.len(); - Some(attributes.clone()) + }); + let http_client_response_body_size = + self.subgraph + .attributes + .http_client_response_body_size + .is_enabled() + .then(|| { + let mut nb_attributes = 0; + let selectors = match &self.subgraph.attributes.http_client_response_body_size { + DefaultedStandardInstrument::Bool(_) + | DefaultedStandardInstrument::Unset => None, + DefaultedStandardInstrument::Extendable { attributes } => { + nb_attributes = attributes.custom.len(); + Some(attributes.clone()) + } + }; + CustomHistogram { + inner: Mutex::new(CustomHistogramInner { + increment: Increment::Custom(None), + histogram: Some( + meter.f64_histogram("http.client.response.body.size").init(), + ), + attributes: Vec::with_capacity(nb_attributes), + selector: Some(Arc::new(SubgraphSelector::SubgraphResponseHeader { + subgraph_response_header: "content-length".to_string(), + redact: None, + default: None, + })), + selectors, + }), } - }; - CustomHistogram { - inner: Mutex::new(CustomHistogramInner { - increment: Increment::Custom(None), - histogram: Some( - meter.f64_histogram("http.client.response.body.size").init(), - ), - attributes: Vec::with_capacity(nb_attributes), - selector: Some(Arc::new(SubgraphSelector::SubgraphResponseHeader { - subgraph_response_header: "content-length".to_string(), - redact: None, - default: None, - })), - selectors, - }), - } - }); + }); SubgraphInstruments { http_client_request_duration, http_client_request_body_size, @@ -346,23 +354,21 @@ impl DefaultForLevel for ActiveRequestsAttributes { } } -#[allow(dead_code)] -#[derive(Clone, Deserialize, JsonSchema, Debug)] +#[derive(Clone, Deserialize, JsonSchema, Debug, Default)] #[serde(deny_unknown_fields, untagged)] enum DefaultedStandardInstrument { + #[default] + Unset, Bool(bool), - Extendable { attributes: Arc }, -} - -impl Default for DefaultedStandardInstrument { - fn default() -> Self { - DefaultedStandardInstrument::Bool(true) - } + Extendable { + attributes: Arc, + }, } impl DefaultedStandardInstrument { fn is_enabled(&self) -> bool { match self { + Self::Unset => false, Self::Bool(enabled) => *enabled, Self::Extendable { .. } => true, } @@ -379,7 +385,18 @@ where kind: TelemetryDataKind, ) { match self { - DefaultedStandardInstrument::Bool(_enabled) => match requirement_level { + DefaultedStandardInstrument::Bool(enabled) if *enabled => match requirement_level { + DefaultAttributeRequirementLevel::None => {} + DefaultAttributeRequirementLevel::Required + | DefaultAttributeRequirementLevel::Recommended => { + let mut attrs = T::default(); + attrs.defaults_for_levels(requirement_level, kind); + *self = Self::Extendable { + attributes: Arc::new(attrs), + } + } + }, + DefaultedStandardInstrument::Unset => match requirement_level { DefaultAttributeRequirementLevel::None => {} DefaultAttributeRequirementLevel::Required | DefaultAttributeRequirementLevel::Recommended => { @@ -393,6 +410,7 @@ where DefaultedStandardInstrument::Extendable { attributes } => { Arc::make_mut(attributes).defaults_for_levels(requirement_level, kind); } + _ => {} } } } @@ -406,21 +424,21 @@ where fn on_request(&self, request: &Self::Request) -> Vec { match self { - Self::Bool(_) => Vec::new(), + Self::Bool(_) | Self::Unset => Vec::new(), Self::Extendable { attributes } => attributes.on_request(request), } } fn on_response(&self, response: &Self::Response) -> Vec { match self { - Self::Bool(_) => Vec::new(), + Self::Bool(_) | Self::Unset => Vec::new(), Self::Extendable { attributes } => attributes.on_response(response), } } fn on_error(&self, error: &BoxError) -> Vec { match self { - Self::Bool(_) => Vec::new(), + Self::Bool(_) | Self::Unset => Vec::new(), Self::Extendable { attributes } => attributes.on_error(error), } } diff --git a/apollo-router/src/plugins/telemetry/mod.rs b/apollo-router/src/plugins/telemetry/mod.rs index c1012e8e42..cd82bb0c17 100644 --- a/apollo-router/src/plugins/telemetry/mod.rs +++ b/apollo-router/src/plugins/telemetry/mod.rs @@ -2116,7 +2116,6 @@ mod tests { "http.response.status_code" = 400, "acme.my_attribute" = "application/json" ); - assert_histogram_exists!("http.server.request.duration", f64); assert_histogram_sum!("acme.request.length", 55.0); let router_req = RouterRequest::fake_builder() @@ -2340,6 +2339,108 @@ mod tests { .await; } + #[tokio::test] + async fn test_custom_subgraph_instruments_level() { + async { + let plugin = create_plugin_with_config(include_str!( + "testdata/custom_instruments_level.router.yaml" + )) + .await; + + let mut mock_bad_request_service = MockSubgraphService::new(); + mock_bad_request_service.expect_call().times(2).returning( + move |req: SubgraphRequest| { + let mut headers = HeaderMap::new(); + headers.insert(CONTENT_TYPE, "application/json".parse().unwrap()); + let errors = vec![ + graphql::Error::builder() + .message("nope".to_string()) + .extension_code("NOPE") + .build(), + graphql::Error::builder() + .message("nok".to_string()) + .extension_code("NOK") + .build(), + ]; + Ok(SubgraphResponse::fake_builder() + .context(req.context) + .status_code(StatusCode::BAD_REQUEST) + .headers(headers) + .errors(errors) + .build()) + }, + ); + let mut bad_request_subgraph_service = + plugin.subgraph_service("test", BoxService::new(mock_bad_request_service)); + let sub_req = http::Request::builder() + .method("POST") + .uri("http://test") + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql") + .body(graphql::Request::builder().query("{ me {name} }").build()) + .unwrap(); + let subgraph_req = SubgraphRequest::fake_builder() + .subgraph_request(sub_req) + .subgraph_name("test".to_string()) + .build(); + + let _router_response = bad_request_subgraph_service + .ready() + .await + .unwrap() + .call(subgraph_req) + .await + .unwrap(); + + assert_counter!( + "acme.subgraph.error_reqs", + 1.0, + graphql_error = opentelemetry::Value::Array(opentelemetry::Array::String(vec![ + "nope".into(), + "nok".into() + ])), + subgraph.name = "test" + ); + let sub_req = http::Request::builder() + .method("POST") + .uri("http://test") + .header("x-custom", "TEST") + .header("conditional-custom", "X") + .header("custom-length", "55") + .header("content-length", "55") + .header("content-type", "application/graphql") + .body(graphql::Request::builder().query("{ me {name} }").build()) + .unwrap(); + let subgraph_req = SubgraphRequest::fake_builder() + .subgraph_request(sub_req) + .subgraph_name("test".to_string()) + .build(); + + let _router_response = bad_request_subgraph_service + .ready() + .await + .unwrap() + .call(subgraph_req) + .await + .unwrap(); + assert_counter!( + "acme.subgraph.error_reqs", + 2.0, + graphql_error = opentelemetry::Value::Array(opentelemetry::Array::String(vec![ + "nope".into(), + "nok".into() + ])), + subgraph.name = "test" + ); + assert_histogram_not_exists!("http.client.request.duration", f64); + } + .with_metrics() + .await; + } + #[tokio::test] async fn test_custom_subgraph_instruments() { async { @@ -2435,7 +2536,6 @@ mod tests { ])), subgraph.name = "test" ); - assert_histogram_exists!("http.client.request.duration", f64); } .with_metrics() .await; diff --git a/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml b/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml index c4d80f0312..72cf175364 100644 --- a/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml +++ b/apollo-router/src/plugins/telemetry/testdata/custom_instruments_level.router.yaml @@ -62,6 +62,7 @@ telemetry: exists: request_header: "x-custom" subgraph: + http.client.request.duration: false acme.subgraph.error_reqs: value: unit type: counter From 82b5215bfd173250d86e972df1a17e49015b8877 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:53:55 +0100 Subject: [PATCH 24/25] update snapshot Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- ...nfiguration__tests__schema_generation.snap | 21 +++++++++++++++++++ 1 file changed, 21 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 f6bcc6bc47..98ebb36d81 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 @@ -6710,6 +6710,9 @@ expression: "&schema" "http.server.active_requests": { "description": "Counter of active requests", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -6749,6 +6752,9 @@ expression: "&schema" "http.server.request.body.size": { "description": "Histogram of server request body size", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -7300,6 +7306,9 @@ expression: "&schema" "http.server.request.duration": { "description": "Histogram of server request duration", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -7851,6 +7860,9 @@ expression: "&schema" "http.server.response.body.size": { "description": "Histogram of server response body size", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -10314,6 +10326,9 @@ expression: "&schema" "http.client.request.body.size": { "description": "Histogram of client request body size", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -11204,6 +11219,9 @@ expression: "&schema" "http.client.request.duration": { "description": "Histogram of client request duration", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, @@ -12094,6 +12112,9 @@ expression: "&schema" "http.client.response.body.size": { "description": "Histogram of client response body size", "anyOf": [ + { + "type": "null" + }, { "type": "boolean" }, From ed41bfb94e2b12862b0852a8a57edd7d91ee1623 Mon Sep 17 00:00:00 2001 From: Coenen Benjamin Date: Tue, 2 Apr 2024 10:46:23 +0200 Subject: [PATCH 25/25] Update .changesets/feat_bnjjj_feat_4319.md Co-authored-by: Gary Pennington --- .changesets/feat_bnjjj_feat_4319.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.changesets/feat_bnjjj_feat_4319.md b/.changesets/feat_bnjjj_feat_4319.md index ff9f543046..e765d0095c 100644 --- a/.changesets/feat_bnjjj_feat_4319.md +++ b/.changesets/feat_bnjjj_feat_4319.md @@ -1,6 +1,9 @@ ### Add support of instruments in configuration for telemetry ([Issue #4319](https://github.com/apollographql/router/issues/4319)) -Add support for custom and standard instruments through the configuration file. You'll be able to add your own custom metrics just using the configuration file. It can be conditional, get values from selectors like headers, context, body. And the metrics can have different types like `histogram` or `counter`. +Add support for custom and standard instruments through the configuration file. You'll be able to add your own custom metrics just using the configuration file. They may: +- be conditional +- get values from selectors, for instance headers, context or body +- have different types like `histogram` or `counter`. Example: