Skip tracing for introspection queries #841

This commit is contained in:
Sunli 2022-10-01 16:17:27 +08:00
parent 5b49a9d891
commit 13a66a5013
12 changed files with 80 additions and 41 deletions

View File

@ -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)

View File

@ -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 }

View File

@ -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)

@ -1 +1 @@
Subproject commit e7f936e6b675b6b237c137eb8690ca49742f2b5b
Subproject commit bb0fa782053271096cf8c61eaf6e670b9d08ae15

View File

@ -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<QueryPathNode<'a>>,
/// 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<SelectionSet>> {
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<SelectionSet>> {
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,

View File

@ -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<Output = Response> + Send + Unpin);

View File

@ -168,24 +168,35 @@ where
info: ResolveInfo<'_>,
next: NextResolve<'_>,
) -> ServerResult<Option<Value>> {
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,
}
}
}

View File

@ -133,22 +133,29 @@ impl Extension for TracingExtension {
info: ResolveInfo<'_>,
next: NextResolve<'_>,
) -> ServerResult<Option<Value>> {
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,
}
}
}

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -34,7 +34,8 @@ impl<T: ObjectType> ContainerType for QueryRoot<T> {
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<T: ObjectType> ContainerType for QueryRoot<T> {
.map(Some);
} else if ctx.item.node.name.node == "__type" {
let (_, type_name) = ctx.param_value::<String>("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<T: ObjectType> ContainerType for QueryRoot<T> {
.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(