use crate::extensions::{Extension, ResolveInfo}; use crate::Variables; use async_graphql_parser::types::ExecutableDocument; use std::collections::BTreeMap; use tracing::{span, Level, Span}; /// Tracing extension /// /// # References /// /// #[derive(Default)] #[cfg_attr(feature = "nightly", doc(cfg(feature = "tracing")))] pub struct Tracing { root: Option, parse: Option, validation: Option, execute: Option, fields: BTreeMap, } impl Extension for Tracing { fn parse_start(&mut self, query_source: &str, _variables: &Variables) { let root_span = span!( target: "async_graphql::graphql", parent: None, Level::INFO, "query", source = %query_source ); let parse_span = span!( target: "async_graphql::graphql", parent: &root_span, Level::INFO, "parse" ); root_span.with_subscriber(|(id, d)| d.enter(id)); self.root.replace(root_span); parse_span.with_subscriber(|(id, d)| d.enter(id)); self.parse.replace(parse_span); } fn parse_end(&mut self, _document: &ExecutableDocument) { self.parse .take() .unwrap() .with_subscriber(|(id, d)| d.exit(id)); } fn validation_start(&mut self) { let parent = self.root.as_ref().unwrap(); let validation_span = span!( target: "async_graphql::graphql", parent: parent, Level::INFO, "validation" ); validation_span.with_subscriber(|(id, d)| d.enter(id)); self.validation.replace(validation_span); } fn validation_end(&mut self) { self.validation .take() .unwrap() .with_subscriber(|(id, d)| d.exit(id)); } fn execution_start(&mut self) { let parent = self.root.as_ref().unwrap(); let execute_span = span!( target: "async_graphql::graphql", parent: parent, Level::INFO, "execute" ); execute_span.with_subscriber(|(id, d)| d.enter(id)); self.execute.replace(execute_span); } fn execution_end(&mut self) { self.execute .take() .unwrap() .with_subscriber(|(id, d)| d.exit(id)); self.root .take() .unwrap() .with_subscriber(|(id, d)| d.exit(id)); } fn resolve_start(&mut self, info: &ResolveInfo<'_>) { let parent_span = match info.resolve_id.parent { Some(parent_id) if parent_id > 0 => self.fields.get(&parent_id).unwrap(), _ => self.execute.as_ref().unwrap(), }; let span = span!( target: "async_graphql::graphql", parent: parent_span, Level::INFO, "field", id = %info.resolve_id.current, path = %info.path_node ); span.with_subscriber(|(id, d)| d.enter(id)); self.fields.insert(info.resolve_id.current, span); } fn resolve_end(&mut self, info: &ResolveInfo<'_>) { if let Some(span) = self.fields.remove(&info.resolve_id.current) { span.with_subscriber(|(id, d)| d.exit(id)); } } }