diff --git a/CHANGELOG.md b/CHANGELOG.md index 4468f68d..330ab6e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [4.0.15] 2022-10-01 + +- Skip tracing for introspection queries. [#841](https://github.com/async-graphql/async-graphql/issues/841) + # [4.0.14] 2022-09-25 - Implement a simple approach to using the link directive. [#1060](https://github.com/async-graphql/async-graphql/pull/1060) diff --git a/Cargo.toml b/Cargo.toml index 13fdc969..61e394d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,7 +70,7 @@ fast_chemail = { version = "0.9.6", optional = true } hashbrown = { version = "0.12.0", optional = true } iso8601-duration = { version = "0.1.0", optional = true } log = { version = "0.4.16", optional = true } -opentelemetry = { version = "0.17.0", optional = true, default-features = false, features = [ +opentelemetry = { version = "0.18.0", optional = true, default-features = false, features = [ "trace", ] } rust_decimal = { version = "1.14.3", optional = true } diff --git a/derive/src/subscription.rs b/derive/src/subscription.rs index 7208608c..2c4634a0 100644 --- a/derive/src/subscription.rs +++ b/derive/src/subscription.rs @@ -336,6 +336,7 @@ pub fn generate( return_type: &<<#stream_ty as #crate_name::futures_util::stream::Stream>::Item as #crate_name::OutputType>::qualified_type_name(), name: field.node.name.node.as_str(), alias: field.node.alias.as_ref().map(|alias| alias.node.as_str()), + is_for_introspection: false, }; let resolve_fut = async { #crate_name::OutputType::resolve(&msg, &ctx_selection_set, &*field) diff --git a/examples b/examples index e7f936e6..bb0fa782 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit e7f936e6b675b6b237c137eb8690ca49742f2b5b +Subproject commit bb0fa782053271096cf8c61eaf6e670b9d08ae15 diff --git a/src/context.rs b/src/context.rs index d8ea9b23..f7643c67 100644 --- a/src/context.rs +++ b/src/context.rs @@ -236,6 +236,8 @@ impl<'a> std::iter::FusedIterator for Parents<'a> {} pub struct ContextBase<'a, T> { /// The current path node being resolved. pub path_node: Option>, + /// If `true` means the current field is for introspection. + pub(crate) is_for_introspection: bool, #[doc(hidden)] pub item: T, #[doc(hidden)] @@ -287,6 +289,7 @@ impl QueryEnv { ) -> ContextBase<'a, T> { ContextBase { path_node, + is_for_introspection: false, item, schema_env, query_env: self, @@ -319,6 +322,7 @@ impl<'a, T> ContextBase<'a, T> { parent: self.path_node.as_ref(), segment: QueryPathSegment::Name(&field.node.response_key().node), }), + is_for_introspection: self.is_for_introspection, item: field, schema_env: self.schema_env, query_env: self.query_env, @@ -332,6 +336,7 @@ impl<'a, T> ContextBase<'a, T> { ) -> ContextBase<'a, &'a Positioned> { ContextBase { path_node: self.path_node, + is_for_introspection: self.is_for_introspection, item: selection_set, schema_env: self.schema_env, query_env: self.query_env, @@ -605,6 +610,7 @@ impl<'a> ContextBase<'a, &'a Positioned> { parent: self.path_node.as_ref(), segment: QueryPathSegment::Index(idx), }), + is_for_introspection: self.is_for_introspection, item: self.item, schema_env: self.schema_env, query_env: self.query_env, diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index d9b40b01..c63dcce3 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -125,6 +125,9 @@ pub struct ResolveInfo<'a> { /// Current field alias pub alias: Option<&'a str>, + + /// If `true` means the current field is for introspection. + pub is_for_introspection: bool, } type RequestFut<'a> = &'a mut (dyn Future + Send + Unpin); diff --git a/src/extensions/opentelemetry.rs b/src/extensions/opentelemetry.rs index f0f5aa67..1f42dd4c 100644 --- a/src/extensions/opentelemetry.rs +++ b/src/extensions/opentelemetry.rs @@ -168,24 +168,35 @@ where info: ResolveInfo<'_>, next: NextResolve<'_>, ) -> ServerResult> { - let attributes = vec![ - KEY_PARENT_TYPE.string(info.parent_type.to_string()), - KEY_RETURN_TYPE.string(info.return_type.to_string()), - ]; - let span = self - .tracer - .span_builder(info.path_node.to_string()) - .with_kind(SpanKind::Server) - .with_attributes(attributes) - .start(&*self.tracer); - next.run(ctx, info) - .with_context(OpenTelemetryContext::current_with_span(span)) - .inspect_err(|err| { - let current_cx = OpenTelemetryContext::current(); - current_cx - .span() - .add_event("error".to_string(), vec![KEY_ERROR.string(err.to_string())]); - }) - .await + let span = if !info.is_for_introspection { + let attributes = vec![ + KEY_PARENT_TYPE.string(info.parent_type.to_string()), + KEY_RETURN_TYPE.string(info.return_type.to_string()), + ]; + Some( + self.tracer + .span_builder(info.path_node.to_string()) + .with_kind(SpanKind::Server) + .with_attributes(attributes) + .start(&*self.tracer), + ) + } else { + None + }; + + let fut = next.run(ctx, info).inspect_err(|err| { + let current_cx = OpenTelemetryContext::current(); + current_cx + .span() + .add_event("error".to_string(), vec![KEY_ERROR.string(err.to_string())]); + }); + + match span { + Some(span) => { + fut.with_context(OpenTelemetryContext::current_with_span(span)) + .await + } + None => fut.await, + } } } diff --git a/src/extensions/tracing.rs b/src/extensions/tracing.rs index 2e4adc8e..da78e733 100644 --- a/src/extensions/tracing.rs +++ b/src/extensions/tracing.rs @@ -133,22 +133,29 @@ impl Extension for TracingExtension { info: ResolveInfo<'_>, next: NextResolve<'_>, ) -> ServerResult> { - let span = span!( - target: "async_graphql::graphql", - Level::INFO, - "field", - path = %info.path_node, - parent_type = %info.parent_type, - return_type = %info.return_type, - ); - next.run(ctx, info) - .map_err(|err| { - tracinglib::info!(target: "async_graphql::graphql", - error = %err.message, - "error"); - err - }) - .instrument(span) - .await + let span = if !info.is_for_introspection { + Some(span!( + target: "async_graphql::graphql", + Level::INFO, + "field", + path = %info.path_node, + parent_type = %info.parent_type, + return_type = %info.return_type, + )) + } else { + None + }; + + let fut = next.run(ctx, info).inspect_err(|err| { + tracinglib::info!( + target: "async_graphql::graphql", + error = %err.message, + "error", + ); + }); + match span { + Some(span) => fut.instrument(span).await, + None => fut.await, + } } } diff --git a/src/resolver_utils/container.rs b/src/resolver_utils/container.rs index ae68f0f0..d86ebd63 100644 --- a/src/resolver_utils/container.rs +++ b/src/resolver_utils/container.rs @@ -239,6 +239,7 @@ impl<'a> Fields<'a> { .alias .as_ref() .map(|alias| alias.node.as_str()), + is_for_introspection: ctx_field.is_for_introspection, }; let resolve_fut = root.resolve_field(&ctx_field); @@ -263,6 +264,7 @@ impl<'a> Fields<'a> { { let ctx_directive = ContextBase { path_node: ctx_field.path_node, + is_for_introspection: false, item: directive, schema_env: ctx_field.schema_env, query_env: ctx_field.query_env, diff --git a/src/resolver_utils/list.rs b/src/resolver_utils/list.rs index be61f41f..54fb7792 100644 --- a/src/resolver_utils/list.rs +++ b/src/resolver_utils/list.rs @@ -26,6 +26,7 @@ pub async fn resolve_list<'a, T: OutputType + 'a>( return_type: &T::qualified_type_name(), name: field.node.name.node.as_str(), alias: field.node.alias.as_ref().map(|alias| alias.node.as_str()), + is_for_introspection: ctx_idx.is_for_introspection, }; let resolve_fut = async { OutputType::resolve(&item, &ctx_idx, field) diff --git a/src/schema.rs b/src/schema.rs index 39769ae3..83c2bf20 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -609,6 +609,7 @@ where // execute let ctx = ContextBase { path_node: None, + is_for_introspection: false, item: &env.operation.node.selection_set, schema_env: &self.env, query_env: &env, diff --git a/src/types/query_root.rs b/src/types/query_root.rs index ac82f5dc..563eaef7 100644 --- a/src/types/query_root.rs +++ b/src/types/query_root.rs @@ -34,7 +34,8 @@ impl ContainerType for QueryRoot { IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly, ) { if ctx.item.node.name.node == "__schema" { - let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + ctx_obj.is_for_introspection = true; let visible_types = ctx.schema_env.registry.find_visible_types(ctx); return OutputType::resolve( &__Schema::new(&ctx.schema_env.registry, &visible_types), @@ -45,7 +46,8 @@ impl ContainerType for QueryRoot { .map(Some); } else if ctx.item.node.name.node == "__type" { let (_, type_name) = ctx.param_value::("name", None)?; - let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + ctx_obj.is_for_introspection = true; let visible_types = ctx.schema_env.registry.find_visible_types(ctx); return OutputType::resolve( &ctx.schema_env @@ -81,7 +83,8 @@ impl ContainerType for QueryRoot { .await?; return Ok(Some(Value::List(res))); } else if ctx.item.node.name.node == "_service" { - let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + let mut ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set); + ctx_obj.is_for_introspection = true; return OutputType::resolve( &Service { sdl: Some(