async_graphql/extensions/
tracing.rs1use std::sync::Arc;
2
3use futures_util::{stream::BoxStream, TryFutureExt};
4use tracing_futures::Instrument;
5use tracinglib::{span, Level};
6
7use crate::{
8 extensions::{
9 Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery, NextRequest,
10 NextResolve, NextSubscribe, NextValidation, ResolveInfo,
11 },
12 parser::types::ExecutableDocument,
13 Response, ServerError, ServerResult, ValidationResult, Value, Variables,
14};
15
16#[cfg_attr(docsrs, doc(cfg(feature = "tracing")))]
41pub struct Tracing;
42
43impl ExtensionFactory for Tracing {
44 fn create(&self) -> Arc<dyn Extension> {
45 Arc::new(TracingExtension)
46 }
47}
48
49struct TracingExtension;
50
51#[async_trait::async_trait]
52impl Extension for TracingExtension {
53 async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
54 next.run(ctx)
55 .instrument(span!(
56 target: "async_graphql::graphql",
57 Level::INFO,
58 "request",
59 ))
60 .await
61 }
62
63 fn subscribe<'s>(
64 &self,
65 ctx: &ExtensionContext<'_>,
66 stream: BoxStream<'s, Response>,
67 next: NextSubscribe<'_>,
68 ) -> BoxStream<'s, Response> {
69 Box::pin(next.run(ctx, stream).instrument(span!(
70 target: "async_graphql::graphql",
71 Level::INFO,
72 "subscribe",
73 )))
74 }
75
76 async fn parse_query(
77 &self,
78 ctx: &ExtensionContext<'_>,
79 query: &str,
80 variables: &Variables,
81 next: NextParseQuery<'_>,
82 ) -> ServerResult<ExecutableDocument> {
83 let span = span!(
84 target: "async_graphql::graphql",
85 Level::INFO,
86 "parse",
87 source = tracinglib::field::Empty
88 );
89 async move {
90 let res = next.run(ctx, query, variables).await;
91 if let Ok(doc) = &res {
92 tracinglib::Span::current()
93 .record("source", ctx.stringify_execute_doc(doc, variables).as_str());
94 }
95 res
96 }
97 .instrument(span)
98 .await
99 }
100
101 async fn validation(
102 &self,
103 ctx: &ExtensionContext<'_>,
104 next: NextValidation<'_>,
105 ) -> Result<ValidationResult, Vec<ServerError>> {
106 let span = span!(
107 target: "async_graphql::graphql",
108 Level::INFO,
109 "validation"
110 );
111 next.run(ctx).instrument(span).await
112 }
113
114 async fn execute(
115 &self,
116 ctx: &ExtensionContext<'_>,
117 operation_name: Option<&str>,
118 next: NextExecute<'_>,
119 ) -> Response {
120 let span = span!(
121 target: "async_graphql::graphql",
122 Level::INFO,
123 "execute"
124 );
125 next.run(ctx, operation_name).instrument(span).await
126 }
127
128 async fn resolve(
129 &self,
130 ctx: &ExtensionContext<'_>,
131 info: ResolveInfo<'_>,
132 next: NextResolve<'_>,
133 ) -> ServerResult<Option<Value>> {
134 let span = if !info.is_for_introspection {
135 Some(span!(
136 target: "async_graphql::graphql",
137 Level::INFO,
138 "field",
139 path = %info.path_node,
140 parent_type = %info.parent_type,
141 return_type = %info.return_type,
142 ))
143 } else {
144 None
145 };
146
147 let fut = next.run(ctx, info).inspect_err(|err| {
148 tracinglib::info!(
149 target: "async_graphql::graphql",
150 error = %err.message,
151 "error",
152 );
153 });
154 match span {
155 Some(span) => fut.instrument(span).await,
156 None => fut.await,
157 }
158 }
159}