From e0f7eb518f080faadaa4a560dfa90048ac3ccc9d Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Sat, 25 Jul 2020 20:57:29 +0100
Subject: [PATCH 01/72] Add tracing support

---
 docs/book/content/SUMMARY.md       |  2 ++
 docs/book/content/tracing/index.md | 38 +++++++++++++++++++++++
 docs/book/tests/Cargo.toml         |  4 +++
 juniper/Cargo.toml                 |  2 ++
 juniper/src/lib.rs                 | 25 ++++++++++++----
 juniper/src/macros/mod.rs          |  2 ++
 juniper/src/macros/tracing.rs      | 48 ++++++++++++++++++++++++++++++
 7 files changed, 116 insertions(+), 5 deletions(-)
 create mode 100644 docs/book/content/tracing/index.md
 create mode 100644 juniper/src/macros/tracing.rs

diff --git a/docs/book/content/SUMMARY.md b/docs/book/content/SUMMARY.md
index 49c542f52..de351f608 100644
--- a/docs/book/content/SUMMARY.md
+++ b/docs/book/content/SUMMARY.md
@@ -25,6 +25,8 @@
     - [Hyper](servers/hyper.md)
   - [Third Party Integrations](servers/third-party.md)
 
+- [Tracing](tracing/index.md)
+
 - [Advanced Topics](advanced/index.md)
 
   - [Introspection](advanced/introspection.md)
diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
new file mode 100644
index 000000000..0a490f59d
--- /dev/null
+++ b/docs/book/content/tracing/index.md
@@ -0,0 +1,38 @@
+# Tracing
+
+Juniper relies on the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+
+!FILENAME Cargo.toml
+
+```toml
+[dependencies]
+tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
+tracing-log = "0.1.1"
+```
+
+## Usage
+
+```rust
+# extern crate tracing;
+# extern crate tracing_subscriber;
+# extern crate tracing_log;
+fn main() {
+    // compatibility with the log crate (unstable)
+    // converts all log records into tracing events
+    tracing_log::LogTracer::init().expect("LogTracer init failed");
+
+    // a builder for `FmtSubscriber`.
+    let subscriber = tracing_subscriber::FmtSubscriber::builder()
+        // all spans/events with a level higher than TRACE
+        // (e.g, debug, info, warn, etc.) will be written to stdout.
+        .with_max_level(tracing::Level::TRACE)
+        // completes the builder.
+        .finish();
+
+    tracing::subscriber::set_global_default(subscriber)
+        .expect("Setting default tracing subscriber failed");
+
+    // ...
+}
+```
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 120f1a894..5943b3bb4 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -20,6 +20,10 @@ skeptic = "0.13"
 serde_json = "1.0.39"
 uuid = "0.8"
 
+tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
+tracing-log = "0.1.1"
+
 [build-dependencies]
 skeptic = "0.13"
 
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index d02b6cf47..59a432f62 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -52,6 +52,8 @@ url = { version = "2", optional = true }
 uuid = { version = "0.8", optional = true }
 graphql-parser = {version = "0.3.0", optional = true }
 
+tracing = {version = "0.1.17", optional = true }
+
 [dev-dependencies]
 bencher = "0.1.2"
 serde_json = { version = "1.0.2" }
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index baba1af2f..663d41637 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -260,7 +260,10 @@ where
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -293,7 +296,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -303,7 +309,10 @@ where
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -338,7 +347,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -348,7 +360,10 @@ where
         let errors = validate_input_values(&variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index 1d4c57006..a8bfcd0f3 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -10,3 +10,5 @@ mod interface;
 mod tests;
 
 pub mod subscription_helpers;
+
+mod tracing;
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
new file mode 100644
index 000000000..676295266
--- /dev/null
+++ b/juniper/src/macros/tracing.rs
@@ -0,0 +1,48 @@
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_internal {
+    ($trace_type:ident; $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        tracing::$trace_type!($($element),*);
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(trace; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_debug {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(debug; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_info {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(info; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_warn {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(warn; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_error {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(error; $($element),*)
+    }};
+}

From 753c80fd9bf618b8297fbb4e27194f2429d4803d Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 10:16:28 -1000
Subject: [PATCH 02/72] Rocket can now compile on stable

---
 juniper_rocket/Makefile.toml | 17 -----------------
 1 file changed, 17 deletions(-)
 delete mode 100644 juniper_rocket/Makefile.toml

diff --git a/juniper_rocket/Makefile.toml b/juniper_rocket/Makefile.toml
deleted file mode 100644
index 8695d6a67..000000000
--- a/juniper_rocket/Makefile.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[tasks.build-verbose]
-condition = { channels = ["nightly"] }
-
-[tasks.build-verbose.windows]
-condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
-
-[tasks.test-verbose]
-condition = { channels = ["nightly"] }
-
-[tasks.test-verbose.windows]
-condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
-
-[tasks.ci-coverage-flow]
-condition = { channels = ["nightly"] }
-
-[tasks.ci-coverage-flow.windows]
-disabled = true
\ No newline at end of file

From c599bae962ece7b546eada2af4bf983ced428519 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 16:48:57 -1000
Subject: [PATCH 03/72] Remove `boxed` in favor of `pin`.

---
 juniper/src/types/scalars.rs             | 2 +-
 juniper_codegen/src/graphql_union/mod.rs | 2 +-
 juniper_codegen/src/util/mod.rs          | 7 +++----
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs
index 53ebc1acc..2f50eefd1 100644
--- a/juniper/src/types/scalars.rs
+++ b/juniper/src/types/scalars.rs
@@ -245,7 +245,7 @@ where
         executor: &'a Executor<Self::Context, S>,
     ) -> crate::BoxFuture<'a, crate::ExecutionResult<S>> {
         use futures::future;
-        future::FutureExt::boxed(future::ready(self.resolve(info, selection_set, executor)))
+        Box::pin(future::ready(self.resolve(info, selection_set, executor)))
     }
 }
 
diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs
index a8658132c..129044526 100644
--- a/juniper_codegen/src/graphql_union/mod.rs
+++ b/juniper_codegen/src/graphql_union/mod.rs
@@ -472,7 +472,7 @@ impl ToTokens for UnionDefinition {
                                 { #expr },
                                 executor.context()
                             );
-                            return ::juniper::futures::future::FutureExt::boxed(async move {
+                            return Box::pin(async move {
                                 match res? {
                                     Some((ctx, r)) => {
                                         let subexec = executor.replaced_context(ctx);
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index ee8b8a781..f91696c91 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -880,8 +880,7 @@ impl GraphQLTypeDefiniton {
                                     Err(e) => Err(e),
                                 }
                             };
-                            use ::juniper::futures::future;
-                            future::FutureExt::boxed(f)
+                            Box::pin(f)
                         },
                     )
                 } else {
@@ -908,7 +907,7 @@ impl GraphQLTypeDefiniton {
                                 Err(e) => Err(e),
                             };
                             use ::juniper::futures::future;
-                            future::FutureExt::boxed(future::ready(v))
+                            Box::pin(future::ready(v))
                         )
                     };
 
@@ -1445,7 +1444,7 @@ impl GraphQLTypeDefiniton {
                 ) -> ::juniper::BoxFuture<'a, ::juniper::ExecutionResult<#scalar>> {
                     use ::juniper::futures::future;
                     let v = ::juniper::GraphQLValue::resolve(self, info, selection_set, executor);
-                    future::FutureExt::boxed(future::ready(v))
+                    Box::pin(future::ready(v))
                 }
             }
         );

From b5569340868004965eb08d52f263894079e4ca19 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 21:43:10 -1000
Subject: [PATCH 04/72] Add tracing support example

---
 Cargo.toml                             |   1 +
 examples/tracing_support/.gitignore    |   4 +
 examples/tracing_support/Cargo.toml    |  14 ++++
 examples/tracing_support/Makefile.toml |  20 +++++
 examples/tracing_support/src/main.rs   | 109 +++++++++++++++++++++++++
 juniper/src/lib.rs                     |   1 +
 6 files changed, 149 insertions(+)
 create mode 100644 examples/tracing_support/.gitignore
 create mode 100644 examples/tracing_support/Cargo.toml
 create mode 100644 examples/tracing_support/Makefile.toml
 create mode 100644 examples/tracing_support/src/main.rs

diff --git a/Cargo.toml b/Cargo.toml
index 79429a10e..c703b0ef3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
   "juniper_codegen",
   "juniper",
   "examples/basic_subscriptions",
+  "examples/tracing_support",
   "examples/warp_async",
   "examples/warp_subscriptions",
   "integration_tests/juniper_tests",
diff --git a/examples/tracing_support/.gitignore b/examples/tracing_support/.gitignore
new file mode 100644
index 000000000..68f412d09
--- /dev/null
+++ b/examples/tracing_support/.gitignore
@@ -0,0 +1,4 @@
+/target/
+**/*.rs.bk
+Cargo.lock
+
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
new file mode 100644
index 000000000..a09c90e1b
--- /dev/null
+++ b/examples/tracing_support/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "tracing_support"
+version = "0.1.0"
+authors = ["Christian Legnitto <christian.legnitto@robinhood.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+# Note instead of a path you would use the proper Juniper version in your own
+# project.
+juniper = { path = "../../juniper", features = ["tracing"] }
+tokio = { version = "0.2.22", features = ["rt-core", "macros", "blocking"] }
diff --git a/examples/tracing_support/Makefile.toml b/examples/tracing_support/Makefile.toml
new file mode 100644
index 000000000..bce0dd4c9
--- /dev/null
+++ b/examples/tracing_support/Makefile.toml
@@ -0,0 +1,20 @@
+[tasks.run]
+disabled = true
+
+[tasks.release]
+disabled = true
+
+[tasks.release-some]
+disabled = true
+
+[tasks.release-local-test]
+disabled = true
+
+[tasks.release-some-local-test]
+disabled = true
+
+[tasks.release-dry-run]
+disabled = true
+
+[tasks.release-some-dry-run]
+disabled = true
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
new file mode 100644
index 000000000..6e6bf2cac
--- /dev/null
+++ b/examples/tracing_support/src/main.rs
@@ -0,0 +1,109 @@
+extern crate juniper;
+extern crate tokio;
+extern crate tracing;
+extern crate tracing_subscriber;
+
+use juniper::{
+    graphql_object, EmptyMutation, EmptySubscription, FieldError, GraphQLEnum, RootNode, Variables,
+};
+use tracing::{trace_span, Instrument as _};
+use tracing_subscriber::EnvFilter;
+
+#[derive(Clone, Copy, Debug)]
+struct Context;
+impl juniper::Context for Context {}
+
+#[derive(Clone, Copy, Debug, GraphQLEnum)]
+enum UserKind {
+    Admin,
+    User,
+    Guest,
+}
+
+#[derive(Clone, Debug)]
+struct User {
+    id: i32,
+    kind: UserKind,
+    name: String,
+}
+
+#[graphql_object(Context = Context)]
+impl User {
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    fn kind(&self) -> UserKind {
+        self.kind
+    }
+
+    fn name(&self) -> &str {
+        &self.name
+    }
+
+    async fn friends(&self) -> Vec<User> {
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+struct Query;
+
+#[graphql_object(Context = Context)]
+impl Query {
+    async fn users() -> Vec<User> {
+        vec![User {
+            id: 1,
+            kind: UserKind::Admin,
+            name: "user1".into(),
+        }]
+    }
+
+    /// Fetch a URL and return the response body text.
+    async fn double(x: i32) -> Result<i32, FieldError> {
+        Ok(x * 2)
+    }
+}
+
+type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>;
+
+fn schema() -> Schema {
+    Schema::new(
+        Query,
+        EmptyMutation::<Context>::new(),
+        EmptySubscription::<Context>::new(),
+    )
+}
+
+#[tokio::main]
+async fn main() {
+    // A builder for `FmtSubscriber`.
+    let subscriber = tracing_subscriber::fmt()
+        // This enables standard env variables such as `RUST_LOG=trace`.
+        .with_env_filter(EnvFilter::from_default_env())
+        // This makes it so we can see all span events.
+        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
+        .finish();
+
+    tracing::subscriber::set_global_default(subscriber).expect("no global subscriber has been set");
+
+    let ctx = Context {};
+    let vars = Variables::new();
+    let root = schema();
+
+    // When run with `RUST_LOG=trace cargo run`, this should output to `stdout`.
+    let query = "{ users { id } }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .await
+        .unwrap();
+
+    // When run with `RUST_LOG=trace cargo run`, this should output to `stdout`.
+    // Note that there is a top-level span of "doubling{42}" as it was set
+    // here. This is useful to attach context to each call to `execute`.
+    let query = "{ double(x: 42) }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .instrument(trace_span!("doubling", "{}", 42))
+        .await
+        .map_err(|e| format!("{:?}", e))
+        .unwrap();
+}
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 663d41637..9eec13705 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -383,6 +383,7 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
+    __juniper_span_trace!("execute_sync");
     execute_sync(
         match format {
             IntrospectionFormat::All => INTROSPECTION_QUERY,

From 0134da3d915dd72825ad9392aa328bcea55c9f16 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 21:54:01 -1000
Subject: [PATCH 05/72] Add some coarse execution tracing

---
 docs/book/tests/Cargo.toml           |  2 +-
 examples/tracing_support/src/main.rs | 13 ++++++-
 juniper/Cargo.toml                   |  2 +-
 juniper/src/lib.rs                   | 13 +++++--
 juniper/src/macros/tracing.rs        | 56 +++++++++++++++++++++++++++-
 5 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 5943b3bb4..282e8a61e 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -20,7 +20,7 @@ skeptic = "0.13"
 serde_json = "1.0.39"
 uuid = "0.8"
 
-tracing = "0.1.17"
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
 tracing-subscriber = "0.2.9"
 tracing-log = "0.1.1"
 
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 6e6bf2cac..8dc638a5e 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -59,6 +59,14 @@ impl Query {
         }]
     }
 
+    fn bob() -> User {
+        User {
+            id: 1,
+            kind: UserKind::Admin,
+            name: "Bob".into(),
+        }
+    }
+
     /// Fetch a URL and return the response body text.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
@@ -104,6 +112,9 @@ async fn main() {
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
         .instrument(trace_span!("doubling", "{}", 42))
         .await
-        .map_err(|e| format!("{:?}", e))
         .unwrap();
+
+    // You can also trace sync execution.
+    let query = "{ bob { name } }";
+    let (_, _errors) = juniper::execute_sync(query, None, &root, &vars, &ctx).unwrap();
 }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 59a432f62..2ca00cca7 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -52,7 +52,7 @@ url = { version = "2", optional = true }
 uuid = { version = "0.8", optional = true }
 graphql-parser = {version = "0.3.0", optional = true }
 
-tracing = {version = "0.1.17", optional = true }
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 9eec13705..2edab1b38 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -245,6 +245,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
+        __juniper_span_trace!("rule_validation");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -257,6 +258,7 @@ where
     let operation = get_operation(&document, operation_name)?;
 
     {
+        __juniper_span_trace!("validate_input_values");
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
@@ -267,6 +269,7 @@ where
         }
     }
 
+    __juniper_span_trace!("execute_sync");
     execute_validated_query(&document, operation, root_node, variables, context)
 }
 
@@ -291,6 +294,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
+        __juniper_span_trace!("rule_validation");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -306,6 +310,7 @@ where
     let operation = get_operation(&document, operation_name)?;
 
     {
+        __juniper_span_trace!("validate_input_values");
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
@@ -316,8 +321,11 @@ where
         }
     }
 
-    executor::execute_validated_query_async(&document, operation, root_node, variables, context)
-        .await
+    let f = executor::execute_validated_query_async(
+        &document, operation, root_node, variables, context,
+    );
+
+    __juniper_instrument_trace!(f, "execute").await
 }
 
 /// Resolve subscription into `ValuesStream`
@@ -383,7 +391,6 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
-    __juniper_span_trace!("execute_sync");
     execute_sync(
         match format {
             IntrospectionFormat::All => INTROSPECTION_QUERY,
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 676295266..bb97479d3 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -1,9 +1,63 @@
+// Macros to instrument future spans.
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_instrument_internal {
+    ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        {
+            use tracing;
+            tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
+        }
+        #[cfg(not(feature = "tracing"))]
+        {
+            $fut
+        }
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_instrument_trace {
+    ($fut:expr, $($element:expr),*) => {{
+        $crate::__juniper_instrument_internal!(trace_span; $fut, $($element),*)
+    }}
+}
+
+// Macros to instrument (non-future) spans.
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_internal {
+    ($trace_type:ident; $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        use tracing;
+        #[cfg(feature = "tracing")]
+        let myspan = tracing::span!(tracing::Level::$trace_type, ($($element),*));
+        #[cfg(feature = "tracing")]
+        let _enter = myspan.enter();
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace {
+    ($($element:expr),*) => {{
+        $crate::__juniper_span_internal!(TRACE; $($element),*)
+    }}
+}
+
+// Macros to instrument events.
+
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
-        tracing::$trace_type!($($element),*);
+        {
+            use tracing;
+            tracing::$trace_type!($($element),*);
+        }
     }};
 }
 

From 5c888a80e289eb7c8e235e20a894ee47151992d5 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 22:19:18 -1000
Subject: [PATCH 06/72] Remove old comment / docs

---
 examples/tracing_support/src/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 8dc638a5e..11dc4e326 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -67,7 +67,7 @@ impl Query {
         }
     }
 
-    /// Fetch a URL and return the response body text.
+    /// Double the provided number.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
     }

From 01b34538ff9832258666030d057acb2228032717 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:07:50 -1000
Subject: [PATCH 07/72] Fix book tests

---
 docs/book/content/tracing/index.md  | 60 +++++++++++++++++++++--------
 docs/book/tests/Cargo.toml          |  9 ++---
 examples/tracing_support/Cargo.toml |  2 +-
 3 files changed, 49 insertions(+), 22 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 0a490f59d..84678fcf6 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -1,38 +1,66 @@
 # Tracing
 
-Juniper relies on the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+Juniper has optional support for the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+
+This feature is off by default and can be enabled via the `tracing` feature.
 
 !FILENAME Cargo.toml
 
 ```toml
 [dependencies]
+juniper = { version = "0.14.2", features = ["default", "tracing"]}
 tracing = "0.1.17"
-tracing-subscriber = "0.2.9"
-tracing-log = "0.1.1"
 ```
 
 ## Usage
 
 ```rust
+# extern crate futures;
+# extern crate juniper;
+# extern crate tokio;
 # extern crate tracing;
 # extern crate tracing_subscriber;
-# extern crate tracing_log;
-fn main() {
-    // compatibility with the log crate (unstable)
-    // converts all log records into tracing events
-    tracing_log::LogTracer::init().expect("LogTracer init failed");
-
-    // a builder for `FmtSubscriber`.
-    let subscriber = tracing_subscriber::FmtSubscriber::builder()
-        // all spans/events with a level higher than TRACE
-        // (e.g, debug, info, warn, etc.) will be written to stdout.
-        .with_max_level(tracing::Level::TRACE)
-        // completes the builder.
+use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variables};
+
+#[derive(Clone, Copy, Debug)]
+struct Query;
+
+#[graphql_object]
+impl Query {
+    async fn foo() -> i32 {
+        42
+    }
+}
+
+type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
+
+
+#[tokio::main]
+async fn main() {
+    // Set up the tracing subscriber.
+    let subscriber = tracing_subscriber::fmt()
+        // This enables standard env variables such as `RUST_LOG=trace`.
+        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+        // This makes it so we can see all span events.
+        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
         .finish();
 
     tracing::subscriber::set_global_default(subscriber)
         .expect("Setting default tracing subscriber failed");
 
-    // ...
+    // Set up GraphQL information.
+    let vars = Variables::new();
+    let root = Schema::new(
+        Query,
+        EmptyMutation::<()>::new(),
+        EmptySubscription::<()>::new(),
+    );
+
+    // When run with `RUST_LOG=trace cargo run`, this should output traces /
+    // span events to `stdout`.
+    let query = "{ foo }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &())
+        .await
+        .unwrap();
 }
 ```
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 282e8a61e..0d54b11a3 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -6,13 +6,13 @@ edition = "2018"
 build = "build.rs"
 
 [dependencies]
-juniper = { path = "../../../juniper" }
+juniper = { path = "../../../juniper", features = ["default", "tracing"] }
 juniper_iron = { path = "../../../juniper_iron" }
 juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
 derive_more = "0.99.7"
 futures = "0.3"
-tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util"] }
+tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util", "macros"] }
 iron = "0.5.0"
 mount = "0.4.0"
 
@@ -20,9 +20,8 @@ skeptic = "0.13"
 serde_json = "1.0.39"
 uuid = "0.8"
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
-tracing-subscriber = "0.2.9"
-tracing-log = "0.1.1"
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index a09c90e1b..d8d314685 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -7,7 +7,7 @@ publish = false
 
 [dependencies]
 tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
 juniper = { path = "../../juniper", features = ["tracing"] }

From 7ac7304e55f2485404229f70869c270d34ec4030 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:10:41 -1000
Subject: [PATCH 08/72] fix up some imports

---
 docs/book/content/tracing/index.md | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 84678fcf6..a1259e5ba 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -15,11 +15,10 @@ tracing = "0.1.17"
 ## Usage
 
 ```rust
-# extern crate futures;
 # extern crate juniper;
-# extern crate tokio;
-# extern crate tracing;
-# extern crate tracing_subscriber;
+extern crate tokio;
+extern crate tracing;
+extern crate tracing_subscriber;
 use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variables};
 
 #[derive(Clone, Copy, Debug)]

From 06687ddf44dee5e7ad7beed4c94a5e4b435e2294 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:12:34 -1000
Subject: [PATCH 09/72] Add back subscriber Cargo.toml instructions

---
 docs/book/content/tracing/index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index a1259e5ba..c0cc1e8d7 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -10,6 +10,7 @@ This feature is off by default and can be enabled via the `tracing` feature.
 [dependencies]
 juniper = { version = "0.14.2", features = ["default", "tracing"]}
 tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
 ```
 
 ## Usage

From 697a7432179dd282c09d1df2648eb5a17586a34b Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 18:26:39 +0100
Subject: [PATCH 10/72] Change trace errors to trace

---
 juniper/src/lib.rs | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 121c6534c..3391ed4bc 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -251,7 +251,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -263,7 +266,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -301,7 +304,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -315,7 +318,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -356,7 +359,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -369,7 +372,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }

From f2977d5085b90e3d4fa78a0972a738e0c003af20 Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 21:24:31 +0100
Subject: [PATCH 11/72] Tracing unit tests

---
 juniper/src/tests/mod.rs         |  4 ++
 juniper/src/tests/trace_tests.rs | 87 ++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+)
 create mode 100644 juniper/src/tests/trace_tests.rs

diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index 3e42ec7da..d624dc5ae 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -11,3 +11,7 @@ mod schema_introspection;
 mod subscriptions;
 #[cfg(test)]
 mod type_info_tests;
+
+#[cfg(test)]
+#[cfg(feature = "tracing")]
+mod trace_tests;
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
new file mode 100644
index 000000000..957b72f22
--- /dev/null
+++ b/juniper/src/tests/trace_tests.rs
@@ -0,0 +1,87 @@
+use tracing_test::*;
+
+use crate::{
+    executor::Variables,
+    schema::model::RootNode,
+    tests::fixtures::starwars::{model::Database, schema::Query},
+    types::scalars::{EmptyMutation, EmptySubscription},
+};
+
+// TODO: waiting for https://github.com/tokio-rs/tracing/pull/793
+// TODO: async tests waiting for https://github.com/tokio-rs/tracing/pull/808
+// TODO: tracing feature needs to be enable when testing
+// cargo test --features tracing
+#[test]
+fn test_trace_execute_clean() {
+    let doc = r#"
+        {
+            hero {
+                name
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let span_rule_validation = span("rule_validation");
+    let span_validate_input_values = span("validate_input_values");
+    let span_execute_sync = span("execute_sync");
+
+    let (subscriber, handle) = subscriber::expect()
+        .new_span(span_rule_validation.clone())
+        .enter(span_rule_validation.clone())
+        .exit(span_rule_validation.clone())
+        .drop_span(span_rule_validation.clone())
+        .new_span(span_validate_input_values.clone())
+        .enter(span_validate_input_values.clone())
+        .exit(span_validate_input_values.clone())
+        .drop_span(span_validate_input_values.clone())
+        .new_span(span_execute_sync.clone())
+        .enter(span_execute_sync.clone())
+        .exit(span_execute_sync.clone())
+        .drop_span(span_execute_sync.clone())
+        .done()
+        .run_with_handle();
+
+    tracing::subscriber::with_default(subscriber, || {
+        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).ok();
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+fn test_trace_execute_with_error() {
+    let doc = r#"
+        {
+            super_hero {
+                name
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let span_rule_validation = span("rule_validation");
+
+    let (subscriber, handle) = subscriber::expect()
+        .new_span(span_rule_validation.clone())
+        .enter(span_rule_validation.clone())
+        .event(event().with_target("juniper"))
+        .exit(span_rule_validation.clone())
+        .drop_span(span_rule_validation.clone())
+        .done()
+        .run_with_handle();
+
+    tracing::subscriber::with_default(subscriber, || {
+        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).err();
+    });
+
+    handle.assert_finished();
+}

From 6c3654c99624585ac34a018ce839e8b5ff4b93ba Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 21:27:04 +0100
Subject: [PATCH 12/72] Tracing unit tests names

---
 juniper/src/tests/trace_tests.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
index 957b72f22..bca0ff08d 100644
--- a/juniper/src/tests/trace_tests.rs
+++ b/juniper/src/tests/trace_tests.rs
@@ -12,7 +12,7 @@ use crate::{
 // TODO: tracing feature needs to be enable when testing
 // cargo test --features tracing
 #[test]
-fn test_trace_execute_clean() {
+fn test_execute_sync_clean() {
     let doc = r#"
         {
             hero {
@@ -54,7 +54,7 @@ fn test_trace_execute_clean() {
 }
 
 #[test]
-fn test_trace_execute_with_error() {
+fn test_execute_sync_with_error() {
     let doc = r#"
         {
             super_hero {

From 7f746de21e9076c5f7e4b4d8922c4574b2695d26 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Wed, 29 Jul 2020 13:51:02 -1000
Subject: [PATCH 13/72] Revert "Rocket can now compile on stable"

This reverts commit 753c80fd9bf618b8297fbb4e27194f2429d4803d.
---
 juniper_rocket/Makefile.toml | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 juniper_rocket/Makefile.toml

diff --git a/juniper_rocket/Makefile.toml b/juniper_rocket/Makefile.toml
new file mode 100644
index 000000000..8695d6a67
--- /dev/null
+++ b/juniper_rocket/Makefile.toml
@@ -0,0 +1,17 @@
+[tasks.build-verbose]
+condition = { channels = ["nightly"] }
+
+[tasks.build-verbose.windows]
+condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
+
+[tasks.test-verbose]
+condition = { channels = ["nightly"] }
+
+[tasks.test-verbose.windows]
+condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
+
+[tasks.ci-coverage-flow]
+condition = { channels = ["nightly"] }
+
+[tasks.ci-coverage-flow.windows]
+disabled = true
\ No newline at end of file

From 88dc9c425e85d7f4e77f68376cbf20ddb6b12d55 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 21:55:05 -1000
Subject: [PATCH 14/72] Fix tracing spans

---
 juniper/src/lib.rs            | 15 +++++++++++++--
 juniper/src/macros/tracing.rs | 17 +++++++----------
 2 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 3391ed4bc..715e1a114 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -124,6 +124,11 @@ pub use {futures, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::BoxFuture;
 
+// This is required by the `tracing` feature.
+#[cfg(feature = "tracing")]
+#[doc(hidden)]
+pub use tracing;
+
 // Depend on juniper_codegen and re-export everything in it.
 // This allows users to just depend on juniper and get the derive
 // functionality automatically.
@@ -242,6 +247,9 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
+    __juniper_span_trace!("execute_sync");
+
+    __juniper_trace!("GraphQLDocument: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -272,7 +280,7 @@ where
         }
     }
 
-    __juniper_span_trace!("execute_sync");
+    __juniper_span_trace!("execute_validated_query");
     execute_validated_query(&document, operation, root_node, variables, context)
 }
 
@@ -294,6 +302,9 @@ where
     SubscriptionT::TypeInfo: Sync,
     S: ScalarValue + Send + Sync,
 {
+    __juniper_span_trace!("execute");
+
+    __juniper_trace!("GraphQLDocument: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -328,7 +339,7 @@ where
         &document, operation, root_node, variables, context,
     );
 
-    __juniper_instrument_trace!(f, "execute").await
+    __juniper_instrument_trace!(f, "execute_validated_query").await
 }
 
 /// Resolve subscription into `ValuesStream`
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index bb97479d3..589b2cda1 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -29,22 +29,20 @@ macro_rules! __juniper_instrument_trace {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_span_internal {
-    ($trace_type:ident; $($element:expr),*) => {{
-        #[cfg(feature = "tracing")]
-        use tracing;
+    ($trace_type:ident; $($element:expr),*) => {
         #[cfg(feature = "tracing")]
-        let myspan = tracing::span!(tracing::Level::$trace_type, ($($element),*));
+        let myspan = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
         #[cfg(feature = "tracing")]
         let _enter = myspan.enter();
-    }};
+    };
 }
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_span_trace {
-    ($($element:expr),*) => {{
-        $crate::__juniper_span_internal!(TRACE; $($element),*)
-    }}
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(TRACE; $($element),*);
+    }
 }
 
 // Macros to instrument events.
@@ -55,8 +53,7 @@ macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
         {
-            use tracing;
-            tracing::$trace_type!($($element),*);
+            $crate::tracing::$trace_type!($($element),*);
         }
     }};
 }

From 8a2f6ec3fb9ceab9bf0581897709e3f4bc74b3f3 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:25:22 -1000
Subject: [PATCH 15/72] Do not include trailing newline on the last error

---
 juniper/src/lib.rs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 715e1a114..ea6a1ddb8 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -217,9 +217,12 @@ impl<'a> fmt::Display for GraphQLError<'a> {
         match self {
             GraphQLError::ParseError(error) => write!(f, "{}", error),
             GraphQLError::ValidationError(errors) => {
-                for error in errors {
-                    writeln!(f, "{}", error)?;
-                }
+                let errors = errors
+                    .iter()
+                    .map(|e| format!("{}", e))
+                    .collect::<Vec<_>>()
+                    .join("\n");
+                write!(f, "{}", errors)?;
                 Ok(())
             }
             GraphQLError::NoOperationProvided => write!(f, "No operation provided"),

From 83fd44d13b53708e3ffdc8b53f5000039d437fed Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:31:05 -1000
Subject: [PATCH 16/72] Standard tracing labels on snake_case

---
 juniper/src/lib.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index ea6a1ddb8..700c396d7 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -252,7 +252,7 @@ where
 {
     __juniper_span_trace!("execute_sync");
 
-    __juniper_trace!("GraphQLDocument: {}", document_source);
+    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -263,7 +263,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -277,7 +277,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -307,7 +307,7 @@ where
 {
     __juniper_span_trace!("execute");
 
-    __juniper_trace!("GraphQLDocument: {}", document_source);
+    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -318,7 +318,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -332,7 +332,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }

From c92dd87c73434f6531d0c5d9535b95952bf7b7d2 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:31:37 -1000
Subject: [PATCH 17/72] Add a validation error case to the tracing example

---
 examples/tracing_support/src/main.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 11dc4e326..502c47ba5 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -115,6 +115,7 @@ async fn main() {
         .unwrap();
 
     // You can also trace sync execution.
-    let query = "{ bob { name } }";
-    let (_, _errors) = juniper::execute_sync(query, None, &root, &vars, &ctx).unwrap();
+    // This should output a validation error in the middle of other spans.
+    let query = "{ bob { field_that_does_not_exist } }";
+    let _ = juniper::execute_sync(query, None, &root, &vars, &ctx);
 }

From 64d8e11cc3b6fe04ccfda35340e081f3546828b7 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Fri, 14 Aug 2020 21:55:18 -1000
Subject: [PATCH 18/72] Use $crate everywhere

---
 juniper/src/macros/tracing.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 589b2cda1..a77eab9b7 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -6,8 +6,7 @@ macro_rules! __juniper_instrument_internal {
     ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
         {
-            use tracing;
-            tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
+            $crate::tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
         }
         #[cfg(not(feature = "tracing"))]
         {

From cdbc0ce42d318cbe0d913a83af058e81e5196a15 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Fri, 14 Aug 2020 21:55:36 -1000
Subject: [PATCH 19/72] Add other levels to span macros

---
 juniper/src/macros/tracing.rs | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index a77eab9b7..05882e398 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -44,6 +44,38 @@ macro_rules! __juniper_span_trace {
     }
 }
 
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_debug {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(DEBUG; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_info {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(INFO; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_warn {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(WARN; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_error {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(ERROR; $($element),*);
+    }
+}
+
 // Macros to instrument events.
 
 #[doc(hidden)]

From 5938345ffa5fc453ff73570bcc182c99bb7a58de Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Sat, 3 Oct 2020 14:13:21 +0100
Subject: [PATCH 20/72] Use the released tracing crate

---
 docs/book/tests/Cargo.toml          | 4 ++--
 examples/tracing_support/Cargo.toml | 4 ++--
 juniper/Cargo.toml                  | 2 +-
 juniper/src/tests/trace_tests.rs    | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 0d54b11a3..16ce9614a 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -20,8 +20,8 @@ skeptic = "0.13"
 serde_json = "1.0.39"
 uuid = "0.8"
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing = "0.1.21"
+tracing-subscriber = "0.2.12"
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index d8d314685..c7c8aa0ed 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -6,8 +6,8 @@ edition = "2018"
 publish = false
 
 [dependencies]
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing = "0.1.21"
+tracing-subscriber = "0.2.12"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
 juniper = { path = "../../juniper", features = ["tracing"] }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 22f8ea22b..bfaf79caf 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -53,7 +53,7 @@ url = { version = "2.0", optional = true }
 uuid = { default-features = false, version = "0.8", optional = true }
 graphql-parser = { version = "0.3", optional = true }
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
+tracing = { version = "0.1.21", optional = true }
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
index bca0ff08d..89d53add8 100644
--- a/juniper/src/tests/trace_tests.rs
+++ b/juniper/src/tests/trace_tests.rs
@@ -8,7 +8,7 @@ use crate::{
 };
 
 // TODO: waiting for https://github.com/tokio-rs/tracing/pull/793
-// TODO: async tests waiting for https://github.com/tokio-rs/tracing/pull/808
+// TODO: async tests
 // TODO: tracing feature needs to be enable when testing
 // cargo test --features tracing
 #[test]

From 8bc07f63a330409ab0fe410d81fbe9c3ceb2dab1 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 19:54:40 -0700
Subject: [PATCH 21/72] Fix email address

---
 examples/tracing_support/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index c7c8aa0ed..408536fdb 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "tracing_support"
 version = "0.1.0"
-authors = ["Christian Legnitto <christian.legnitto@robinhood.com>"]
+authors = ["Christian Legnitto <christian@legnitto.com>"]
 edition = "2018"
 publish = false
 

From 82497d3918244c28615fc03e7754bb950dc1c5b4 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 19:56:49 -0700
Subject: [PATCH 22/72] Standardize on snake case for tracing

---
 juniper/src/lib.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index b980b7e88..a76610e9a 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -373,7 +373,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -386,7 +386,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {:?}", gql_error);
 
             return Err(gql_error);
         }

From 79102a3ed063da69c55975259bc3039ff80529fb Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Sat, 25 Jul 2020 20:57:29 +0100
Subject: [PATCH 23/72] Add tracing support

---
 docs/book/content/SUMMARY.md       |  2 ++
 docs/book/content/tracing/index.md | 38 +++++++++++++++++++++++
 docs/book/tests/Cargo.toml         |  4 +++
 juniper/Cargo.toml                 |  2 ++
 juniper/src/lib.rs                 | 25 ++++++++++++----
 juniper/src/macros/mod.rs          |  4 +++
 juniper/src/macros/tracing.rs      | 48 ++++++++++++++++++++++++++++++
 7 files changed, 118 insertions(+), 5 deletions(-)
 create mode 100644 docs/book/content/tracing/index.md
 create mode 100644 juniper/src/macros/tracing.rs

diff --git a/docs/book/content/SUMMARY.md b/docs/book/content/SUMMARY.md
index 24ea171ff..4c1203078 100644
--- a/docs/book/content/SUMMARY.md
+++ b/docs/book/content/SUMMARY.md
@@ -25,6 +25,8 @@
     - [Hyper](servers/hyper.md)
   - [Third Party Integrations](servers/third-party.md)
 
+- [Tracing](tracing/index.md)
+
 - [Advanced Topics](advanced/index.md)
 
   - [Introspection](advanced/introspection.md)
diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
new file mode 100644
index 000000000..0a490f59d
--- /dev/null
+++ b/docs/book/content/tracing/index.md
@@ -0,0 +1,38 @@
+# Tracing
+
+Juniper relies on the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+
+!FILENAME Cargo.toml
+
+```toml
+[dependencies]
+tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
+tracing-log = "0.1.1"
+```
+
+## Usage
+
+```rust
+# extern crate tracing;
+# extern crate tracing_subscriber;
+# extern crate tracing_log;
+fn main() {
+    // compatibility with the log crate (unstable)
+    // converts all log records into tracing events
+    tracing_log::LogTracer::init().expect("LogTracer init failed");
+
+    // a builder for `FmtSubscriber`.
+    let subscriber = tracing_subscriber::FmtSubscriber::builder()
+        // all spans/events with a level higher than TRACE
+        // (e.g, debug, info, warn, etc.) will be written to stdout.
+        .with_max_level(tracing::Level::TRACE)
+        // completes the builder.
+        .finish();
+
+    tracing::subscriber::set_global_default(subscriber)
+        .expect("Setting default tracing subscriber failed");
+
+    // ...
+}
+```
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 7456182c7..44a41eb0e 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -19,6 +19,10 @@ serde_json = "1.0"
 tokio = { version = "0.2", features = ["blocking", "macros", "rt-core", "rt-util", "stream"] }
 uuid = "0.8"
 
+tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
+tracing-log = "0.1.1"
+
 [build-dependencies]
 skeptic = "0.13"
 
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index d13e8750e..f3bdee01a 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -50,6 +50,8 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
+tracing = {version = "0.1.17", optional = true }
+
 [dev-dependencies]
 bencher = "0.1.2"
 pretty_assertions = "0.6.1"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 3cab9b96c..ca80d2767 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -246,7 +246,10 @@ where
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -279,7 +282,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -289,7 +295,10 @@ where
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -324,7 +333,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -334,7 +346,10 @@ where
         let errors = validate_input_values(&variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index b38958dae..c052a5eb2 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -4,3 +4,7 @@ pub mod helper;
 
 #[cfg(test)]
 mod tests;
+
+pub mod subscription_helpers;
+
+mod tracing;
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
new file mode 100644
index 000000000..676295266
--- /dev/null
+++ b/juniper/src/macros/tracing.rs
@@ -0,0 +1,48 @@
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_internal {
+    ($trace_type:ident; $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        tracing::$trace_type!($($element),*);
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(trace; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_debug {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(debug; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_info {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(info; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_warn {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(warn; $($element),*)
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_trace_error {
+    ($($element:expr),*) => {{
+        $crate::__juniper_trace_internal!(error; $($element),*)
+    }};
+}

From 6a16cf857ff21a87638d58cb86bf73bd372e99b7 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 10:16:28 -1000
Subject: [PATCH 24/72] Rocket can now compile on stable

---
 juniper_rocket/Makefile.toml | 17 -----------------
 1 file changed, 17 deletions(-)
 delete mode 100644 juniper_rocket/Makefile.toml

diff --git a/juniper_rocket/Makefile.toml b/juniper_rocket/Makefile.toml
deleted file mode 100644
index 8695d6a67..000000000
--- a/juniper_rocket/Makefile.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[tasks.build-verbose]
-condition = { channels = ["nightly"] }
-
-[tasks.build-verbose.windows]
-condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
-
-[tasks.test-verbose]
-condition = { channels = ["nightly"] }
-
-[tasks.test-verbose.windows]
-condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
-
-[tasks.ci-coverage-flow]
-condition = { channels = ["nightly"] }
-
-[tasks.ci-coverage-flow.windows]
-disabled = true
\ No newline at end of file

From 11bb5e2ac7a24f1bf27c134190705695649a4857 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 21:43:10 -1000
Subject: [PATCH 25/72] Add tracing support example

---
 Cargo.toml                             |   1 +
 examples/tracing_support/.gitignore    |   4 +
 examples/tracing_support/Cargo.toml    |  14 ++++
 examples/tracing_support/Makefile.toml |  20 +++++
 examples/tracing_support/src/main.rs   | 109 +++++++++++++++++++++++++
 juniper/src/lib.rs                     |   1 +
 6 files changed, 149 insertions(+)
 create mode 100644 examples/tracing_support/.gitignore
 create mode 100644 examples/tracing_support/Cargo.toml
 create mode 100644 examples/tracing_support/Makefile.toml
 create mode 100644 examples/tracing_support/src/main.rs

diff --git a/Cargo.toml b/Cargo.toml
index ddb54b5bb..22c710236 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,7 @@ members = [
   "juniper_codegen",
   "juniper",
   "examples/basic_subscriptions",
+  "examples/tracing_support",
   "examples/warp_async",
   "examples/warp_subscriptions",
   "examples/actix_subscriptions",
diff --git a/examples/tracing_support/.gitignore b/examples/tracing_support/.gitignore
new file mode 100644
index 000000000..68f412d09
--- /dev/null
+++ b/examples/tracing_support/.gitignore
@@ -0,0 +1,4 @@
+/target/
+**/*.rs.bk
+Cargo.lock
+
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
new file mode 100644
index 000000000..a09c90e1b
--- /dev/null
+++ b/examples/tracing_support/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "tracing_support"
+version = "0.1.0"
+authors = ["Christian Legnitto <christian.legnitto@robinhood.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+# Note instead of a path you would use the proper Juniper version in your own
+# project.
+juniper = { path = "../../juniper", features = ["tracing"] }
+tokio = { version = "0.2.22", features = ["rt-core", "macros", "blocking"] }
diff --git a/examples/tracing_support/Makefile.toml b/examples/tracing_support/Makefile.toml
new file mode 100644
index 000000000..bce0dd4c9
--- /dev/null
+++ b/examples/tracing_support/Makefile.toml
@@ -0,0 +1,20 @@
+[tasks.run]
+disabled = true
+
+[tasks.release]
+disabled = true
+
+[tasks.release-some]
+disabled = true
+
+[tasks.release-local-test]
+disabled = true
+
+[tasks.release-some-local-test]
+disabled = true
+
+[tasks.release-dry-run]
+disabled = true
+
+[tasks.release-some-dry-run]
+disabled = true
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
new file mode 100644
index 000000000..6e6bf2cac
--- /dev/null
+++ b/examples/tracing_support/src/main.rs
@@ -0,0 +1,109 @@
+extern crate juniper;
+extern crate tokio;
+extern crate tracing;
+extern crate tracing_subscriber;
+
+use juniper::{
+    graphql_object, EmptyMutation, EmptySubscription, FieldError, GraphQLEnum, RootNode, Variables,
+};
+use tracing::{trace_span, Instrument as _};
+use tracing_subscriber::EnvFilter;
+
+#[derive(Clone, Copy, Debug)]
+struct Context;
+impl juniper::Context for Context {}
+
+#[derive(Clone, Copy, Debug, GraphQLEnum)]
+enum UserKind {
+    Admin,
+    User,
+    Guest,
+}
+
+#[derive(Clone, Debug)]
+struct User {
+    id: i32,
+    kind: UserKind,
+    name: String,
+}
+
+#[graphql_object(Context = Context)]
+impl User {
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    fn kind(&self) -> UserKind {
+        self.kind
+    }
+
+    fn name(&self) -> &str {
+        &self.name
+    }
+
+    async fn friends(&self) -> Vec<User> {
+        vec![]
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+struct Query;
+
+#[graphql_object(Context = Context)]
+impl Query {
+    async fn users() -> Vec<User> {
+        vec![User {
+            id: 1,
+            kind: UserKind::Admin,
+            name: "user1".into(),
+        }]
+    }
+
+    /// Fetch a URL and return the response body text.
+    async fn double(x: i32) -> Result<i32, FieldError> {
+        Ok(x * 2)
+    }
+}
+
+type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>;
+
+fn schema() -> Schema {
+    Schema::new(
+        Query,
+        EmptyMutation::<Context>::new(),
+        EmptySubscription::<Context>::new(),
+    )
+}
+
+#[tokio::main]
+async fn main() {
+    // A builder for `FmtSubscriber`.
+    let subscriber = tracing_subscriber::fmt()
+        // This enables standard env variables such as `RUST_LOG=trace`.
+        .with_env_filter(EnvFilter::from_default_env())
+        // This makes it so we can see all span events.
+        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
+        .finish();
+
+    tracing::subscriber::set_global_default(subscriber).expect("no global subscriber has been set");
+
+    let ctx = Context {};
+    let vars = Variables::new();
+    let root = schema();
+
+    // When run with `RUST_LOG=trace cargo run`, this should output to `stdout`.
+    let query = "{ users { id } }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .await
+        .unwrap();
+
+    // When run with `RUST_LOG=trace cargo run`, this should output to `stdout`.
+    // Note that there is a top-level span of "doubling{42}" as it was set
+    // here. This is useful to attach context to each call to `execute`.
+    let query = "{ double(x: 42) }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .instrument(trace_span!("doubling", "{}", 42))
+        .await
+        .map_err(|e| format!("{:?}", e))
+        .unwrap();
+}
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index ca80d2767..3f1941ac4 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -369,6 +369,7 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
+    __juniper_span_trace!("execute_sync");
     execute_sync(
         match format {
             IntrospectionFormat::All => INTROSPECTION_QUERY,

From 57ecb56010ea6bb6d59c34f778120809a8a4ca07 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 21:54:01 -1000
Subject: [PATCH 26/72] Add some coarse execution tracing

---
 docs/book/tests/Cargo.toml           |  2 +-
 examples/tracing_support/src/main.rs | 13 ++++++-
 juniper/Cargo.toml                   |  2 +-
 juniper/src/lib.rs                   | 13 +++++--
 juniper/src/macros/tracing.rs        | 56 +++++++++++++++++++++++++++-
 5 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 44a41eb0e..18155e865 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -19,7 +19,7 @@ serde_json = "1.0"
 tokio = { version = "0.2", features = ["blocking", "macros", "rt-core", "rt-util", "stream"] }
 uuid = "0.8"
 
-tracing = "0.1.17"
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
 tracing-subscriber = "0.2.9"
 tracing-log = "0.1.1"
 
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 6e6bf2cac..8dc638a5e 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -59,6 +59,14 @@ impl Query {
         }]
     }
 
+    fn bob() -> User {
+        User {
+            id: 1,
+            kind: UserKind::Admin,
+            name: "Bob".into(),
+        }
+    }
+
     /// Fetch a URL and return the response body text.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
@@ -104,6 +112,9 @@ async fn main() {
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
         .instrument(trace_span!("doubling", "{}", 42))
         .await
-        .map_err(|e| format!("{:?}", e))
         .unwrap();
+
+    // You can also trace sync execution.
+    let query = "{ bob { name } }";
+    let (_, _errors) = juniper::execute_sync(query, None, &root, &vars, &ctx).unwrap();
 }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index f3bdee01a..b52b2b2a9 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -50,7 +50,7 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing = {version = "0.1.17", optional = true }
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 3f1941ac4..ca98e841c 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -231,6 +231,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
+        __juniper_span_trace!("rule_validation");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -243,6 +244,7 @@ where
     let operation = get_operation(&document, operation_name)?;
 
     {
+        __juniper_span_trace!("validate_input_values");
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
@@ -253,6 +255,7 @@ where
         }
     }
 
+    __juniper_span_trace!("execute_sync");
     execute_validated_query(&document, operation, root_node, variables, context)
 }
 
@@ -277,6 +280,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
+        __juniper_span_trace!("rule_validation");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -292,6 +296,7 @@ where
     let operation = get_operation(&document, operation_name)?;
 
     {
+        __juniper_span_trace!("validate_input_values");
         let errors = validate_input_values(variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
@@ -302,8 +307,11 @@ where
         }
     }
 
-    executor::execute_validated_query_async(&document, operation, root_node, variables, context)
-        .await
+    let f = executor::execute_validated_query_async(
+        &document, operation, root_node, variables, context,
+    );
+
+    __juniper_instrument_trace!(f, "execute").await
 }
 
 /// Resolve subscription into `ValuesStream`
@@ -369,7 +377,6 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
-    __juniper_span_trace!("execute_sync");
     execute_sync(
         match format {
             IntrospectionFormat::All => INTROSPECTION_QUERY,
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 676295266..bb97479d3 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -1,9 +1,63 @@
+// Macros to instrument future spans.
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_instrument_internal {
+    ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        {
+            use tracing;
+            tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
+        }
+        #[cfg(not(feature = "tracing"))]
+        {
+            $fut
+        }
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_instrument_trace {
+    ($fut:expr, $($element:expr),*) => {{
+        $crate::__juniper_instrument_internal!(trace_span; $fut, $($element),*)
+    }}
+}
+
+// Macros to instrument (non-future) spans.
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_internal {
+    ($trace_type:ident; $($element:expr),*) => {{
+        #[cfg(feature = "tracing")]
+        use tracing;
+        #[cfg(feature = "tracing")]
+        let myspan = tracing::span!(tracing::Level::$trace_type, ($($element),*));
+        #[cfg(feature = "tracing")]
+        let _enter = myspan.enter();
+    }};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace {
+    ($($element:expr),*) => {{
+        $crate::__juniper_span_internal!(TRACE; $($element),*)
+    }}
+}
+
+// Macros to instrument events.
+
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
-        tracing::$trace_type!($($element),*);
+        {
+            use tracing;
+            tracing::$trace_type!($($element),*);
+        }
     }};
 }
 

From cff66787e9be36ef8700eac5afb58655cbd24202 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 22:19:18 -1000
Subject: [PATCH 27/72] Remove old comment / docs

---
 examples/tracing_support/src/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 8dc638a5e..11dc4e326 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -67,7 +67,7 @@ impl Query {
         }
     }
 
-    /// Fetch a URL and return the response body text.
+    /// Double the provided number.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
     }

From cb3ee27a88409d053c8bba19b16f477897658514 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:07:50 -1000
Subject: [PATCH 28/72] Fix book tests

---
 docs/book/content/tracing/index.md  | 60 +++++++++++++++++++++--------
 docs/book/tests/Cargo.toml          |  8 ++--
 examples/tracing_support/Cargo.toml |  2 +-
 3 files changed, 49 insertions(+), 21 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 0a490f59d..84678fcf6 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -1,38 +1,66 @@
 # Tracing
 
-Juniper relies on the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+Juniper has optional support for the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+
+This feature is off by default and can be enabled via the `tracing` feature.
 
 !FILENAME Cargo.toml
 
 ```toml
 [dependencies]
+juniper = { version = "0.14.2", features = ["default", "tracing"]}
 tracing = "0.1.17"
-tracing-subscriber = "0.2.9"
-tracing-log = "0.1.1"
 ```
 
 ## Usage
 
 ```rust
+# extern crate futures;
+# extern crate juniper;
+# extern crate tokio;
 # extern crate tracing;
 # extern crate tracing_subscriber;
-# extern crate tracing_log;
-fn main() {
-    // compatibility with the log crate (unstable)
-    // converts all log records into tracing events
-    tracing_log::LogTracer::init().expect("LogTracer init failed");
-
-    // a builder for `FmtSubscriber`.
-    let subscriber = tracing_subscriber::FmtSubscriber::builder()
-        // all spans/events with a level higher than TRACE
-        // (e.g, debug, info, warn, etc.) will be written to stdout.
-        .with_max_level(tracing::Level::TRACE)
-        // completes the builder.
+use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variables};
+
+#[derive(Clone, Copy, Debug)]
+struct Query;
+
+#[graphql_object]
+impl Query {
+    async fn foo() -> i32 {
+        42
+    }
+}
+
+type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
+
+
+#[tokio::main]
+async fn main() {
+    // Set up the tracing subscriber.
+    let subscriber = tracing_subscriber::fmt()
+        // This enables standard env variables such as `RUST_LOG=trace`.
+        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+        // This makes it so we can see all span events.
+        .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL)
         .finish();
 
     tracing::subscriber::set_global_default(subscriber)
         .expect("Setting default tracing subscriber failed");
 
-    // ...
+    // Set up GraphQL information.
+    let vars = Variables::new();
+    let root = Schema::new(
+        Query,
+        EmptyMutation::<()>::new(),
+        EmptySubscription::<()>::new(),
+    );
+
+    // When run with `RUST_LOG=trace cargo run`, this should output traces /
+    // span events to `stdout`.
+    let query = "{ foo }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &())
+        .await
+        .unwrap();
 }
 ```
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 18155e865..52ef407f6 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -6,12 +6,13 @@ authors = ["Magnus Hallin <mhallin@fastmail.com>"]
 build = "build.rs"
 
 [dependencies]
-juniper = { path = "../../../juniper" }
+juniper = { path = "../../../juniper", features = ["default", "tracing"] }
 juniper_iron = { path = "../../../juniper_iron" }
 juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
 derive_more = "0.99"
 futures = "0.3"
+tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util", "macros"] }
 iron = "0.5"
 mount = "0.4"
 skeptic = "0.13"
@@ -19,9 +20,8 @@ serde_json = "1.0"
 tokio = { version = "0.2", features = ["blocking", "macros", "rt-core", "rt-util", "stream"] }
 uuid = "0.8"
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
-tracing-subscriber = "0.2.9"
-tracing-log = "0.1.1"
+tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index a09c90e1b..d8d314685 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -7,7 +7,7 @@ publish = false
 
 [dependencies]
 tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
 juniper = { path = "../../juniper", features = ["tracing"] }

From 0d52c50ba2ffecdcf5871f17ef13afc9d40bbfb7 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:10:41 -1000
Subject: [PATCH 29/72] fix up some imports

---
 docs/book/content/tracing/index.md | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 84678fcf6..a1259e5ba 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -15,11 +15,10 @@ tracing = "0.1.17"
 ## Usage
 
 ```rust
-# extern crate futures;
 # extern crate juniper;
-# extern crate tokio;
-# extern crate tracing;
-# extern crate tracing_subscriber;
+extern crate tokio;
+extern crate tracing;
+extern crate tracing_subscriber;
 use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variables};
 
 #[derive(Clone, Copy, Debug)]

From 7f2450996b34e6175f39816db681dbeffd9fe825 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Tue, 28 Jul 2020 23:12:34 -1000
Subject: [PATCH 30/72] Add back subscriber Cargo.toml instructions

---
 docs/book/content/tracing/index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index a1259e5ba..c0cc1e8d7 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -10,6 +10,7 @@ This feature is off by default and can be enabled via the `tracing` feature.
 [dependencies]
 juniper = { version = "0.14.2", features = ["default", "tracing"]}
 tracing = "0.1.17"
+tracing-subscriber = "0.2.9"
 ```
 
 ## Usage

From cb7ab0cf34a44c5583c23674bf716349d7442704 Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 18:26:39 +0100
Subject: [PATCH 31/72] Change trace errors to trace

---
 juniper/src/lib.rs | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index ca98e841c..cd37858c6 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -237,7 +237,10 @@ where
 
         let errors = ctx.into_errors();
         if !errors.is_empty() {
-            return Err(GraphQLError::ValidationError(errors));
+            let gql_error = GraphQLError::ValidationError(errors);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
+
+            return Err(gql_error);
         }
     }
 
@@ -249,7 +252,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -287,7 +290,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -301,7 +304,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -342,7 +345,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -355,7 +358,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace_error!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("GraphQLError: {:?}", gql_error);
 
             return Err(gql_error);
         }

From 7b02ccc6b88709888baf00afd85e9819e2440854 Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 21:24:31 +0100
Subject: [PATCH 32/72] Tracing unit tests

---
 juniper/src/tests/mod.rs         |  4 ++
 juniper/src/tests/trace_tests.rs | 87 ++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+)
 create mode 100644 juniper/src/tests/trace_tests.rs

diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index 3e42ec7da..d624dc5ae 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -11,3 +11,7 @@ mod schema_introspection;
 mod subscriptions;
 #[cfg(test)]
 mod type_info_tests;
+
+#[cfg(test)]
+#[cfg(feature = "tracing")]
+mod trace_tests;
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
new file mode 100644
index 000000000..957b72f22
--- /dev/null
+++ b/juniper/src/tests/trace_tests.rs
@@ -0,0 +1,87 @@
+use tracing_test::*;
+
+use crate::{
+    executor::Variables,
+    schema::model::RootNode,
+    tests::fixtures::starwars::{model::Database, schema::Query},
+    types::scalars::{EmptyMutation, EmptySubscription},
+};
+
+// TODO: waiting for https://github.com/tokio-rs/tracing/pull/793
+// TODO: async tests waiting for https://github.com/tokio-rs/tracing/pull/808
+// TODO: tracing feature needs to be enable when testing
+// cargo test --features tracing
+#[test]
+fn test_trace_execute_clean() {
+    let doc = r#"
+        {
+            hero {
+                name
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let span_rule_validation = span("rule_validation");
+    let span_validate_input_values = span("validate_input_values");
+    let span_execute_sync = span("execute_sync");
+
+    let (subscriber, handle) = subscriber::expect()
+        .new_span(span_rule_validation.clone())
+        .enter(span_rule_validation.clone())
+        .exit(span_rule_validation.clone())
+        .drop_span(span_rule_validation.clone())
+        .new_span(span_validate_input_values.clone())
+        .enter(span_validate_input_values.clone())
+        .exit(span_validate_input_values.clone())
+        .drop_span(span_validate_input_values.clone())
+        .new_span(span_execute_sync.clone())
+        .enter(span_execute_sync.clone())
+        .exit(span_execute_sync.clone())
+        .drop_span(span_execute_sync.clone())
+        .done()
+        .run_with_handle();
+
+    tracing::subscriber::with_default(subscriber, || {
+        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).ok();
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+fn test_trace_execute_with_error() {
+    let doc = r#"
+        {
+            super_hero {
+                name
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let span_rule_validation = span("rule_validation");
+
+    let (subscriber, handle) = subscriber::expect()
+        .new_span(span_rule_validation.clone())
+        .enter(span_rule_validation.clone())
+        .event(event().with_target("juniper"))
+        .exit(span_rule_validation.clone())
+        .drop_span(span_rule_validation.clone())
+        .done()
+        .run_with_handle();
+
+    tracing::subscriber::with_default(subscriber, || {
+        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).err();
+    });
+
+    handle.assert_finished();
+}

From 4000f1e82e37aa47b0db1209bdfb8608a6692157 Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Wed, 29 Jul 2020 21:27:04 +0100
Subject: [PATCH 33/72] Tracing unit tests names

---
 juniper/src/tests/trace_tests.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
index 957b72f22..bca0ff08d 100644
--- a/juniper/src/tests/trace_tests.rs
+++ b/juniper/src/tests/trace_tests.rs
@@ -12,7 +12,7 @@ use crate::{
 // TODO: tracing feature needs to be enable when testing
 // cargo test --features tracing
 #[test]
-fn test_trace_execute_clean() {
+fn test_execute_sync_clean() {
     let doc = r#"
         {
             hero {
@@ -54,7 +54,7 @@ fn test_trace_execute_clean() {
 }
 
 #[test]
-fn test_trace_execute_with_error() {
+fn test_execute_sync_with_error() {
     let doc = r#"
         {
             super_hero {

From e968c4b21ecd22288a6b838372cd1bb5e475b346 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Wed, 29 Jul 2020 13:51:02 -1000
Subject: [PATCH 34/72] Revert "Rocket can now compile on stable"

This reverts commit 753c80fd9bf618b8297fbb4e27194f2429d4803d.
---
 juniper_rocket/Makefile.toml | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 juniper_rocket/Makefile.toml

diff --git a/juniper_rocket/Makefile.toml b/juniper_rocket/Makefile.toml
new file mode 100644
index 000000000..8695d6a67
--- /dev/null
+++ b/juniper_rocket/Makefile.toml
@@ -0,0 +1,17 @@
+[tasks.build-verbose]
+condition = { channels = ["nightly"] }
+
+[tasks.build-verbose.windows]
+condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
+
+[tasks.test-verbose]
+condition = { channels = ["nightly"] }
+
+[tasks.test-verbose.windows]
+condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc" } }
+
+[tasks.ci-coverage-flow]
+condition = { channels = ["nightly"] }
+
+[tasks.ci-coverage-flow.windows]
+disabled = true
\ No newline at end of file

From 58fed76d8046c32dd5df19acfbadb4c0e955a3be Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 21:55:05 -1000
Subject: [PATCH 35/72] Fix tracing spans

---
 juniper/src/lib.rs            | 15 +++++++++++++--
 juniper/src/macros/tracing.rs | 17 +++++++----------
 2 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index cd37858c6..5239f1aeb 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -106,6 +106,11 @@ pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::{BoxFuture, LocalBoxFuture};
 
+// This is required by the `tracing` feature.
+#[cfg(feature = "tracing")]
+#[doc(hidden)]
+pub use tracing;
+
 // Depend on juniper_codegen and re-export everything in it.
 // This allows users to just depend on juniper and get the derive
 // functionality automatically.
@@ -228,6 +233,9 @@ where
     MutationT: GraphQLType<S, Context = QueryT::Context>,
     SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
 {
+    __juniper_span_trace!("execute_sync");
+
+    __juniper_trace!("GraphQLDocument: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -258,7 +266,7 @@ where
         }
     }
 
-    __juniper_span_trace!("execute_sync");
+    __juniper_span_trace!("execute_validated_query");
     execute_validated_query(&document, operation, root_node, variables, context)
 }
 
@@ -280,6 +288,9 @@ where
     SubscriptionT::TypeInfo: Sync,
     S: ScalarValue + Send + Sync,
 {
+    __juniper_span_trace!("execute");
+
+    __juniper_trace!("GraphQLDocument: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -314,7 +325,7 @@ where
         &document, operation, root_node, variables, context,
     );
 
-    __juniper_instrument_trace!(f, "execute").await
+    __juniper_instrument_trace!(f, "execute_validated_query").await
 }
 
 /// Resolve subscription into `ValuesStream`
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index bb97479d3..589b2cda1 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -29,22 +29,20 @@ macro_rules! __juniper_instrument_trace {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_span_internal {
-    ($trace_type:ident; $($element:expr),*) => {{
-        #[cfg(feature = "tracing")]
-        use tracing;
+    ($trace_type:ident; $($element:expr),*) => {
         #[cfg(feature = "tracing")]
-        let myspan = tracing::span!(tracing::Level::$trace_type, ($($element),*));
+        let myspan = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
         #[cfg(feature = "tracing")]
         let _enter = myspan.enter();
-    }};
+    };
 }
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_span_trace {
-    ($($element:expr),*) => {{
-        $crate::__juniper_span_internal!(TRACE; $($element),*)
-    }}
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(TRACE; $($element),*);
+    }
 }
 
 // Macros to instrument events.
@@ -55,8 +53,7 @@ macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
         {
-            use tracing;
-            tracing::$trace_type!($($element),*);
+            $crate::tracing::$trace_type!($($element),*);
         }
     }};
 }

From 7b5ad7e85d72f07942222a7b51a2a62def3823df Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:25:22 -1000
Subject: [PATCH 36/72] Do not include trailing newline on the last error

---
 juniper/src/lib.rs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 5239f1aeb..0d9cc2c45 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -203,9 +203,12 @@ impl<'a> fmt::Display for GraphQLError<'a> {
         match self {
             GraphQLError::ParseError(error) => write!(f, "{}", error),
             GraphQLError::ValidationError(errors) => {
-                for error in errors {
-                    writeln!(f, "{}", error)?;
-                }
+                let errors = errors
+                    .iter()
+                    .map(|e| format!("{}", e))
+                    .collect::<Vec<_>>()
+                    .join("\n");
+                write!(f, "{}", errors)?;
                 Ok(())
             }
             GraphQLError::NoOperationProvided => write!(f, "No operation provided"),

From c34a1c234400448fdb0174efa698dddb4ad017b8 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:31:05 -1000
Subject: [PATCH 37/72] Standard tracing labels on snake_case

---
 juniper/src/lib.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 0d9cc2c45..54a45d386 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -238,7 +238,7 @@ where
 {
     __juniper_span_trace!("execute_sync");
 
-    __juniper_trace!("GraphQLDocument: {}", document_source);
+    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -249,7 +249,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -263,7 +263,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -293,7 +293,7 @@ where
 {
     __juniper_span_trace!("execute");
 
-    __juniper_trace!("GraphQLDocument: {}", document_source);
+    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -304,7 +304,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }
@@ -318,7 +318,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {}", gql_error);
 
             return Err(gql_error);
         }

From 26407e2aeeae07c73f1f7fd025b8e101b2975c87 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Thu, 13 Aug 2020 22:31:37 -1000
Subject: [PATCH 38/72] Add a validation error case to the tracing example

---
 examples/tracing_support/src/main.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 11dc4e326..502c47ba5 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -115,6 +115,7 @@ async fn main() {
         .unwrap();
 
     // You can also trace sync execution.
-    let query = "{ bob { name } }";
-    let (_, _errors) = juniper::execute_sync(query, None, &root, &vars, &ctx).unwrap();
+    // This should output a validation error in the middle of other spans.
+    let query = "{ bob { field_that_does_not_exist } }";
+    let _ = juniper::execute_sync(query, None, &root, &vars, &ctx);
 }

From 94279d171c370ce2d13025d7213e744312cbf368 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Fri, 14 Aug 2020 21:55:18 -1000
Subject: [PATCH 39/72] Use $crate everywhere

---
 juniper/src/macros/tracing.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 589b2cda1..a77eab9b7 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -6,8 +6,7 @@ macro_rules! __juniper_instrument_internal {
     ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
         #[cfg(feature = "tracing")]
         {
-            use tracing;
-            tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
+            $crate::tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
         }
         #[cfg(not(feature = "tracing"))]
         {

From bf35077e46db530db6d4aaf138305f07886b6aaa Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian.legnitto@robinhood.com>
Date: Fri, 14 Aug 2020 21:55:36 -1000
Subject: [PATCH 40/72] Add other levels to span macros

---
 juniper/src/macros/tracing.rs | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index a77eab9b7..05882e398 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -44,6 +44,38 @@ macro_rules! __juniper_span_trace {
     }
 }
 
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_debug {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(DEBUG; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_info {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(INFO; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_warn {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(WARN; $($element),*);
+    }
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __juniper_span_trace_error {
+    ($($element:expr),*) => {
+        $crate::__juniper_span_internal!(ERROR; $($element),*);
+    }
+}
+
 // Macros to instrument events.
 
 #[doc(hidden)]

From 23650161e0b6e74776685072ab9826a9ee844034 Mon Sep 17 00:00:00 2001
From: Mihai Dinculescu <mihai.dinculescu@outlook.com>
Date: Sat, 3 Oct 2020 14:13:21 +0100
Subject: [PATCH 41/72] Use the released tracing crate

---
 docs/book/tests/Cargo.toml          | 4 ++--
 examples/tracing_support/Cargo.toml | 4 ++--
 juniper/Cargo.toml                  | 2 +-
 juniper/src/tests/trace_tests.rs    | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 52ef407f6..cd6bd6526 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -20,8 +20,8 @@ serde_json = "1.0"
 tokio = { version = "0.2", features = ["blocking", "macros", "rt-core", "rt-util", "stream"] }
 uuid = "0.8"
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing = "0.1.21"
+tracing-subscriber = "0.2.12"
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index d8d314685..c7c8aa0ed 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -6,8 +6,8 @@ edition = "2018"
 publish = false
 
 [dependencies]
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing" }
-tracing-subscriber = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing"}
+tracing = "0.1.21"
+tracing-subscriber = "0.2.12"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
 juniper = { path = "../../juniper", features = ["tracing"] }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index b52b2b2a9..96fcbca73 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -50,7 +50,7 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing = { git = "https://github.com/tokio-rs/tracing.git", branch = "davidbarsky/add-instrument-trait-to-tracing", optional = true }
+tracing = { version = "0.1.21", optional = true }
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
index bca0ff08d..89d53add8 100644
--- a/juniper/src/tests/trace_tests.rs
+++ b/juniper/src/tests/trace_tests.rs
@@ -8,7 +8,7 @@ use crate::{
 };
 
 // TODO: waiting for https://github.com/tokio-rs/tracing/pull/793
-// TODO: async tests waiting for https://github.com/tokio-rs/tracing/pull/808
+// TODO: async tests
 // TODO: tracing feature needs to be enable when testing
 // cargo test --features tracing
 #[test]

From ca744b58df76dd3901f150a942503d65af95d943 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 19:54:40 -0700
Subject: [PATCH 42/72] Fix email address

---
 examples/tracing_support/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index c7c8aa0ed..408536fdb 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "tracing_support"
 version = "0.1.0"
-authors = ["Christian Legnitto <christian.legnitto@robinhood.com>"]
+authors = ["Christian Legnitto <christian@legnitto.com>"]
 edition = "2018"
 publish = false
 

From 62acd3f66b5e7e500307a9b00a84383d81a2cb2f Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 19:56:49 -0700
Subject: [PATCH 43/72] Standardize on snake case for tracing

---
 juniper/src/lib.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 54a45d386..148795e10 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -359,7 +359,7 @@ where
         let errors = ctx.into_errors();
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {:?}", gql_error);
 
             return Err(gql_error);
         }
@@ -372,7 +372,7 @@ where
 
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
-            __juniper_trace!("GraphQLError: {:?}", gql_error);
+            __juniper_trace!("validation_error: {:?}", gql_error);
 
             return Err(gql_error);
         }

From 72a28d4ebd0b0c4974ba2085fd87304ab11729c0 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 20:24:50 -0700
Subject: [PATCH 44/72] Remove rustfmt option causing breakage

---
 rustfmt.toml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/rustfmt.toml b/rustfmt.toml
index 8586c40cb..8a1751f67 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,2 +1 @@
-merge_imports = true
 use_field_init_shorthand = true

From 2977b1f375968e4cb0eabf4b36912906cefc1c07 Mon Sep 17 00:00:00 2001
From: Christian Legnitto <christian@legnitto.com>
Date: Wed, 28 Oct 2020 20:25:12 -0700
Subject: [PATCH 45/72] Remove subscription helper mod

---
 juniper/src/macros/mod.rs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index c052a5eb2..ff6dd3d8a 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -5,6 +5,4 @@ pub mod helper;
 #[cfg(test)]
 mod tests;
 
-pub mod subscription_helpers;
-
 mod tracing;

From 7a1dfdc90e1f694b659c0438420e39ba5e9e994f Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 29 Apr 2021 10:22:47 +0300
Subject: [PATCH 46/72] Fix type checking on fragments and implement e2e tests
 to cover this case

---
 .../juniper_tests/src/issue_922.rs            | 121 ++++++++++++++++++
 integration_tests/juniper_tests/src/lib.rs    |   2 +
 juniper/src/types/async_await.rs              |   6 +-
 3 files changed, 128 insertions(+), 1 deletion(-)
 create mode 100644 integration_tests/juniper_tests/src/issue_922.rs

diff --git a/integration_tests/juniper_tests/src/issue_922.rs b/integration_tests/juniper_tests/src/issue_922.rs
new file mode 100644
index 000000000..b141b5549
--- /dev/null
+++ b/integration_tests/juniper_tests/src/issue_922.rs
@@ -0,0 +1,121 @@
+use juniper::*;
+
+struct Query;
+
+#[juniper::graphql_object]
+impl Query {
+    fn characters(executor: &Executor) -> Vec<CharacterValue> {
+        executor.look_ahead();
+
+        vec![
+            Into::into(Human {
+                id: 0,
+                name: "human-32".to_owned(),
+            }),
+            Into::into(Droid {
+                id: 1,
+                name: "R2-D2".to_owned(),
+            }),
+        ]
+    }
+}
+
+#[juniper::graphql_interface(for = [Human, Droid])]
+trait Character {
+    fn id(&self) -> i32;
+
+    fn name(&self) -> String;
+}
+
+#[derive(juniper::GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+struct Human {
+    pub id: i32,
+    pub name: String,
+}
+
+#[juniper::graphql_interface]
+impl Character for Human {
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    fn name(&self) -> String {
+        self.name.clone()
+    }
+}
+
+#[derive(juniper::GraphQLObject)]
+#[graphql(impl = CharacterValue)]
+struct Droid {
+    pub id: i32,
+    pub name: String,
+}
+
+#[juniper::graphql_interface]
+impl Character for Droid {
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    fn name(&self) -> String {
+        self.name.clone()
+    }
+}
+
+type Schema = juniper::RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
+
+#[tokio::test]
+async fn test_fragment_on_interface() {
+    let query = r#"
+        query Query {
+            characters {
+                ...CharacterFragment
+            }
+        }
+
+        fragment CharacterFragment on Character {
+            __typename
+            ... on Human {
+                id
+                name
+            }
+            ... on Droid {
+                id
+                name
+            }
+        }
+    "#;
+
+    let (res, errors) = juniper::execute(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .await
+    .unwrap();
+
+    assert_eq!(errors.len(), 0);
+
+    let res = match res {
+        juniper::Value::Object(res) => res,
+        _ => panic!("Object should be returned"),
+    };
+    let characters = res.get_field_value("characters");
+    assert!(characters.is_some(), "No characters returned");
+
+    if let juniper::Value::List(values) = characters.unwrap() {
+        for obj in values {
+            if let juniper::Value::Object(obj) = obj {
+                assert!(obj.contains_field("id"), "id field should be present");
+                assert!(obj.contains_field("name"), "name field should be present");
+            } else {
+                assert!(false, "List should contain value");
+            }
+        }
+    } else {
+        assert!(false, "List should be returned")
+    }
+}
diff --git a/integration_tests/juniper_tests/src/lib.rs b/integration_tests/juniper_tests/src/lib.rs
index ce6123d32..a4a9c97fe 100644
--- a/integration_tests/juniper_tests/src/lib.rs
+++ b/integration_tests/juniper_tests/src/lib.rs
@@ -21,4 +21,6 @@ mod issue_500;
 #[cfg(test)]
 mod issue_914;
 #[cfg(test)]
+mod issue_922;
+#[cfg(test)]
 mod pre_parse;
diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs
index e1f6e6e1c..54d5882c1 100644
--- a/juniper/src/types/async_await.rs
+++ b/juniper/src/types/async_await.rs
@@ -306,7 +306,11 @@ where
                 );
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
-                if fragment.type_condition.item == concrete_type_name {
+                let type_name = instance.type_name(info);
+
+                if fragment.type_condition.item == concrete_type_name
+                    || Some(fragment.type_condition.item) == type_name
+                {
                     let sub_result = instance
                         .resolve_into_type_async(
                             info,

From d427f806202b5cf352e4856838d24e887448e5e4 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 29 Apr 2021 11:23:25 +0300
Subject: [PATCH 47/72] Add CHANGELOG note

---
 juniper/CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 273a0087a..1816e9618 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- No changes yet
+- Fixed fields on interface not being resolved when used with fragments ([#923](https://github.com/graphql-rust/juniper/pull/923))
 
 # [[0.15.4] 2021-04-03](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.15.4)
 

From 64cb389f2823e7d64143f19bdf6460a08fb4ef4f Mon Sep 17 00:00:00 2001
From: tyranron <tyranron@gmail.com>
Date: Thu, 29 Apr 2021 14:17:58 +0300
Subject: [PATCH 48/72] Some corrections [skip ci]

---
 integration_tests/juniper_tests/src/issue_922.rs | 1 -
 juniper/src/types/async_await.rs                 | 1 -
 2 files changed, 2 deletions(-)

diff --git a/integration_tests/juniper_tests/src/issue_922.rs b/integration_tests/juniper_tests/src/issue_922.rs
index b141b5549..6249f34c8 100644
--- a/integration_tests/juniper_tests/src/issue_922.rs
+++ b/integration_tests/juniper_tests/src/issue_922.rs
@@ -6,7 +6,6 @@ struct Query;
 impl Query {
     fn characters(executor: &Executor) -> Vec<CharacterValue> {
         executor.look_ahead();
-
         vec![
             Into::into(Human {
                 id: 0,
diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs
index 54d5882c1..4fd16aad7 100644
--- a/juniper/src/types/async_await.rs
+++ b/juniper/src/types/async_await.rs
@@ -307,7 +307,6 @@ where
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
                 let type_name = instance.type_name(info);
-
                 if fragment.type_condition.item == concrete_type_name
                     || Some(fragment.type_condition.item) == type_name
                 {

From a34067386ee7f340ee49648c2eade8656b6c54f2 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 29 Apr 2021 15:10:27 +0300
Subject: [PATCH 49/72] Fix interface fields not being resolved for sync
 context and rework assertions in tests

---
 .../juniper_tests/src/issue_922.rs            | 52 +++++++++++--------
 juniper/src/types/base.rs                     |  6 ++-
 2 files changed, 36 insertions(+), 22 deletions(-)

diff --git a/integration_tests/juniper_tests/src/issue_922.rs b/integration_tests/juniper_tests/src/issue_922.rs
index 6249f34c8..df8d285c5 100644
--- a/integration_tests/juniper_tests/src/issue_922.rs
+++ b/integration_tests/juniper_tests/src/issue_922.rs
@@ -4,8 +4,7 @@ struct Query;
 
 #[juniper::graphql_object]
 impl Query {
-    fn characters(executor: &Executor) -> Vec<CharacterValue> {
-        executor.look_ahead();
+    fn characters() -> Vec<CharacterValue> {
         vec![
             Into::into(Human {
                 id: 0,
@@ -98,23 +97,34 @@ async fn test_fragment_on_interface() {
 
     assert_eq!(errors.len(), 0);
 
-    let res = match res {
-        juniper::Value::Object(res) => res,
-        _ => panic!("Object should be returned"),
-    };
-    let characters = res.get_field_value("characters");
-    assert!(characters.is_some(), "No characters returned");
-
-    if let juniper::Value::List(values) = characters.unwrap() {
-        for obj in values {
-            if let juniper::Value::Object(obj) = obj {
-                assert!(obj.contains_field("id"), "id field should be present");
-                assert!(obj.contains_field("name"), "name field should be present");
-            } else {
-                assert!(false, "List should contain value");
-            }
-        }
-    } else {
-        assert!(false, "List should be returned")
-    }
+    assert_eq!(
+        res,
+        graphql_value!({
+            "characters": [
+                {"__typename": "Human", "id": 0, "name": "human-32"},
+                {"__typename": "Droid", "id": 1, "name": "R2-D2"}
+            ]
+        }),
+    );
+
+    let (res, errors) = juniper::execute_sync(
+        query,
+        None,
+        &Schema::new(Query, EmptyMutation::new(), EmptySubscription::new()),
+        &Variables::new(),
+        &(),
+    )
+    .unwrap();
+
+    assert_eq!(errors.len(), 0);
+
+    assert_eq!(
+        res,
+        graphql_value!({
+            "characters": [
+                {"__typename": "Human", "id": 0, "name": "human-32"},
+                {"__typename": "Droid", "id": 1, "name": "R2-D2"}
+            ]
+        }),
+    );
 }
diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs
index 61d32ca84..3288db5a5 100644
--- a/juniper/src/types/base.rs
+++ b/juniper/src/types/base.rs
@@ -549,7 +549,11 @@ where
                 if let Some(ref type_condition) = fragment.type_condition {
                     // Check whether the type matches the type condition.
                     let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
-                    if type_condition.item == concrete_type_name {
+                    let type_name = instance.type_name(info);
+
+                    if type_condition.item == concrete_type_name
+                        || Some(type_condition.item) == type_name
+                    {
                         let sub_result = instance.resolve_into_type(
                             info,
                             type_condition.item,

From 28ebd319c5375a03d6e094bfa16f1b6e28a6459c Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 29 Apr 2021 15:43:33 +0300
Subject: [PATCH 50/72] Minor corrections

---
 juniper/src/types/async_await.rs |  1 +
 juniper/src/types/base.rs        | 12 ++++++------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs
index 4fd16aad7..54d5882c1 100644
--- a/juniper/src/types/async_await.rs
+++ b/juniper/src/types/async_await.rs
@@ -307,6 +307,7 @@ where
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
                 let type_name = instance.type_name(info);
+
                 if fragment.type_condition.item == concrete_type_name
                     || Some(fragment.type_condition.item) == type_name
                 {
diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs
index 3288db5a5..5b55c283b 100644
--- a/juniper/src/types/base.rs
+++ b/juniper/src/types/base.rs
@@ -515,7 +515,11 @@ where
                 );
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
-                if fragment.type_condition.item == concrete_type_name {
+                let type_name = instance.type_name(info);
+
+                if fragment.type_condition.item == concrete_type_name
+                    || Some(fragment.type_condition.item) == type_name
+                {
                     let sub_result = instance.resolve_into_type(
                         info,
                         fragment.type_condition.item,
@@ -549,11 +553,7 @@ where
                 if let Some(ref type_condition) = fragment.type_condition {
                     // Check whether the type matches the type condition.
                     let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
-                    let type_name = instance.type_name(info);
-
-                    if type_condition.item == concrete_type_name
-                        || Some(type_condition.item) == type_name
-                    {
+                    if type_condition.item == concrete_type_name {
                         let sub_result = instance.resolve_into_type(
                             info,
                             type_condition.item,

From 9e46dce9c03f59350efe223426267211706075a3 Mon Sep 17 00:00:00 2001
From: tyranron <tyranron@gmail.com>
Date: Fri, 30 Apr 2021 13:11:43 +0300
Subject: [PATCH 51/72] Small style corrections

---
 integration_tests/juniper_tests/src/issue_922.rs | 10 ++++------
 juniper/CHANGELOG.md                             |  2 +-
 juniper/src/types/async_await.rs                 |  1 -
 juniper/src/types/base.rs                        |  1 -
 4 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/integration_tests/juniper_tests/src/issue_922.rs b/integration_tests/juniper_tests/src/issue_922.rs
index df8d285c5..35de87faf 100644
--- a/integration_tests/juniper_tests/src/issue_922.rs
+++ b/integration_tests/juniper_tests/src/issue_922.rs
@@ -96,14 +96,13 @@ async fn test_fragment_on_interface() {
     .unwrap();
 
     assert_eq!(errors.len(), 0);
-
     assert_eq!(
         res,
         graphql_value!({
             "characters": [
                 {"__typename": "Human", "id": 0, "name": "human-32"},
-                {"__typename": "Droid", "id": 1, "name": "R2-D2"}
-            ]
+                {"__typename": "Droid", "id": 1, "name": "R2-D2"},
+            ],
         }),
     );
 
@@ -117,14 +116,13 @@ async fn test_fragment_on_interface() {
     .unwrap();
 
     assert_eq!(errors.len(), 0);
-
     assert_eq!(
         res,
         graphql_value!({
             "characters": [
                 {"__typename": "Human", "id": 0, "name": "human-32"},
-                {"__typename": "Droid", "id": 1, "name": "R2-D2"}
-            ]
+                {"__typename": "Droid", "id": 1, "name": "R2-D2"},
+            ],
         }),
     );
 }
diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 1816e9618..f71fce95b 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- Fixed fields on interface not being resolved when used with fragments ([#923](https://github.com/graphql-rust/juniper/pull/923))
+- Fix fields on interfaces not being resolved when used with fragments ([#923](https://github.com/graphql-rust/juniper/pull/923))
 
 # [[0.15.4] 2021-04-03](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.15.4)
 
diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs
index 54d5882c1..4fd16aad7 100644
--- a/juniper/src/types/async_await.rs
+++ b/juniper/src/types/async_await.rs
@@ -307,7 +307,6 @@ where
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
                 let type_name = instance.type_name(info);
-
                 if fragment.type_condition.item == concrete_type_name
                     || Some(fragment.type_condition.item) == type_name
                 {
diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs
index 5b55c283b..014af5de5 100644
--- a/juniper/src/types/base.rs
+++ b/juniper/src/types/base.rs
@@ -516,7 +516,6 @@ where
 
                 let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
                 let type_name = instance.type_name(info);
-
                 if fragment.type_condition.item == concrete_type_name
                     || Some(fragment.type_condition.item) == type_name
                 {

From d0558a4c1faf0463c47788925677e91dd3921cc8 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Mon, 12 Jul 2021 15:37:09 +0300
Subject: [PATCH 52/72] Impl async resolve tracing

---
 docs/book/content/tracing/index.md         |   2 +-
 docs/book/tests/Cargo.toml                 |   2 +-
 examples/tracing_support/Cargo.toml        |   2 +-
 juniper/Cargo.toml                         |   5 +-
 juniper/src/lib.rs                         |   4 +-
 juniper/src/macros/tracing.rs              |  10 +--
 juniper/src/tests/mod.rs                   |   2 +-
 juniper_codegen/Cargo.toml                 |   3 +
 juniper_codegen/src/derive_enum.rs         |   1 +
 juniper_codegen/src/derive_input_object.rs |   1 +
 juniper_codegen/src/derive_object.rs       |   1 +
 juniper_codegen/src/impl_object.rs         |   1 +
 juniper_codegen/src/util/mod.rs            | 100 ++++++++++++++++++++-
 13 files changed, 120 insertions(+), 14 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index c0cc1e8d7..8fbd820ce 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -8,7 +8,7 @@ This feature is off by default and can be enabled via the `tracing` feature.
 
 ```toml
 [dependencies]
-juniper = { version = "0.14.2", features = ["default", "tracing"]}
+juniper = { version = "0.14.2", features = ["default", "traced"]}
 tracing = "0.1.17"
 tracing-subscriber = "0.2.9"
 ```
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 5842f2227..4ad9c2882 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Magnus Hallin <mhallin@fastmail.com>"]
 build = "build.rs"
 
 [dependencies]
-juniper = { path = "../../../juniper", features = ["default", "tracing"] }
+juniper = { path = "../../../juniper", features = ["default", "traced"] }
 juniper_iron = { path = "../../../juniper_iron" }
 juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index 408536fdb..7a36370ae 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -10,5 +10,5 @@ tracing = "0.1.21"
 tracing-subscriber = "0.2.12"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
-juniper = { path = "../../juniper", features = ["tracing"] }
+juniper = { path = "../../juniper", features = ["traced"] }
 tokio = { version = "0.2.22", features = ["rt-core", "macros", "blocking"] }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 99d40eaea..ebd27c51e 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -30,9 +30,10 @@ expose-test-schema = ["anyhow", "serde_json"]
 graphql-parser-integration = ["graphql-parser"]
 scalar-naivetime = []
 schema-language = ["graphql-parser-integration"]
+traced = ["tracing", "juniper_codegen/traced"]
 
 [dependencies]
-juniper_codegen = { version = "0.15.7", path = "../juniper_codegen"  }
+juniper_codegen = { path = "../juniper_codegen" }
 
 anyhow = { version = "1.0.32", optional = true, default-features = false }
 async-trait = "0.1.39"
@@ -51,7 +52,7 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing = { version = "0.1.21", optional = true }
+tracing = { version = "0.1.23", optional = true }
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index ad4ce444e..5a241b7e4 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -106,8 +106,8 @@ pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::{BoxFuture, LocalBoxFuture};
 
-// This is required by the `tracing` feature.
-#[cfg(feature = "tracing")]
+// This is required by the `traced` feature.
+#[cfg(feature = "traced")]
 #[doc(hidden)]
 pub use tracing;
 
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 05882e398..0cffa63ff 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -4,11 +4,11 @@
 #[macro_export]
 macro_rules! __juniper_instrument_internal {
     ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
-        #[cfg(feature = "tracing")]
+        #[cfg(feature = "traced")]
         {
             $crate::tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
         }
-        #[cfg(not(feature = "tracing"))]
+        #[cfg(not(feature = "traced"))]
         {
             $fut
         }
@@ -29,9 +29,9 @@ macro_rules! __juniper_instrument_trace {
 #[macro_export]
 macro_rules! __juniper_span_internal {
     ($trace_type:ident; $($element:expr),*) => {
-        #[cfg(feature = "tracing")]
+        #[cfg(feature = "traced")]
         let myspan = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
-        #[cfg(feature = "tracing")]
+        #[cfg(feature = "traced")]
         let _enter = myspan.enter();
     };
 }
@@ -82,7 +82,7 @@ macro_rules! __juniper_span_trace_error {
 #[macro_export]
 macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
-        #[cfg(feature = "tracing")]
+        #[cfg(feature = "traced")]
         {
             $crate::tracing::$trace_type!($($element),*);
         }
diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index d624dc5ae..d07539238 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -13,5 +13,5 @@ mod subscriptions;
 mod type_info_tests;
 
 #[cfg(test)]
-#[cfg(feature = "tracing")]
+#[cfg(feature = "traced")]
 mod trace_tests;
diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml
index 28d44c131..54061eb98 100644
--- a/juniper_codegen/Cargo.toml
+++ b/juniper_codegen/Cargo.toml
@@ -17,6 +17,9 @@ travis-ci = { repository = "graphql-rust/juniper" }
 [lib]
 proc-macro = true
 
+[features]
+traced = []
+
 [dependencies]
 proc-macro-error = "1.0.2"
 proc-macro2 = "1.0.1"
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index 458bc43ea..a1298ba69 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -94,6 +94,7 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: field_attrs.deprecation.map(SpanContainer::into_inner),
+                tracing: field_attrs.tracing,
                 resolver_code,
                 is_type_inferred: true,
                 is_async: false,
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index 48d63dbc3..78f36124e 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -90,6 +90,7 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: None,
+                tracing: field_attrs.tracing,
                 resolver_code,
                 is_type_inferred: true,
                 is_async: false,
diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs
index 6d0af8fa4..356867226 100644
--- a/juniper_codegen/src/derive_object.rs
+++ b/juniper_codegen/src/derive_object.rs
@@ -82,6 +82,7 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: field_attrs.deprecation.map(SpanContainer::into_inner),
+                tracing: field_attrs.tracing,
                 resolver_code,
                 default: None,
                 is_type_inferred: true,
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 9de0a7551..30ef327d5 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -175,6 +175,7 @@ fn create(
                 args,
                 description: attrs.description.map(SpanContainer::into_inner),
                 deprecation: attrs.deprecation.map(SpanContainer::into_inner),
+                tracing: attrs.tracing,
                 resolver_code,
                 is_type_inferred: false,
                 is_async,
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 31c173ca3..3667ca65f 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -141,6 +141,51 @@ fn get_deprecated_meta_list(list: &MetaList) -> DeprecationAttr {
     DeprecationAttr { reason: None }
 }
 
+#[derive(Debug, Default)]
+pub struct TracingAttr {
+    name: Option<SpanContainer<syn::LitStr>>,
+    skip: HashMap<String, SpanContainer<syn::Ident>>,
+    no_trace: bool,
+}
+
+impl Parse for TracingAttr {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let mut attr = TracingAttr::default();
+
+        let content;
+        syn::parenthesized!(content in input);
+        while !content.is_empty() {
+            let name = content.parse::<syn::Ident>()?;
+
+            match name.to_string().as_str() {
+                "name" => {
+                    content.parse::<token::Eq>()?;
+                    let val: syn::LitStr = content.parse()?;
+                    attr.name = Some(SpanContainer::new(name.span(), Some(val.span()), val));
+                }
+                "skip" => {
+                    let skipped_fields;
+                    syn::parenthesized!(skipped_fields in content);
+                    while !skipped_fields.is_empty() {
+                        let field: syn::Ident = skipped_fields.parse()?;
+                        attr.skip.insert(field.to_string(), SpanContainer::new(field.span(), None, field));
+
+                        skipped_fields.parse::<token::Comma>().ok();
+                    }
+                }
+                "no_trace" => {
+                    attr.no_trace = true;
+                }
+                _ => return Err(syn::Error::new(name.span(), "unknown attribute")),
+            }
+
+            // Discard trailing comma.
+            content.parse::<token::Comma>().ok();
+        }
+        Ok(attr)
+    }
+}
+
 // Gets doc comment.
 pub fn get_doc_comment(attrs: &[Attribute]) -> Option<SpanContainer<String>> {
     if let Some(items) = get_doc_attr(attrs) {
@@ -590,6 +635,8 @@ pub struct FieldAttributes {
     pub arguments: HashMap<String, FieldAttributeArgument>,
     /// Only relevant for object input objects.
     pub default: Option<SpanContainer<Option<syn::Expr>>>,
+    // Only relevant when `tracing` feature enabled
+    pub tracing: Option<TracingAttr>
 }
 
 impl Parse for FieldAttributes {
@@ -652,6 +699,12 @@ impl FieldAttributes {
             output.deprecation = deprecation;
         }
 
+        let tracing_attr = attrs.iter().find(|attr| attr.path.is_ident("traced"));
+
+        if let Some(attr) = tracing_attr {
+            output.tracing = Some(attr.parse_args()?);
+        }
+
         Ok(output)
     }
 
@@ -674,6 +727,7 @@ pub struct GraphQLTypeDefinitionField {
     pub _type: syn::Type,
     pub description: Option<String>,
     pub deprecation: Option<DeprecationAttr>,
+    pub tracing: Option<TracingAttr>,
     pub args: Vec<GraphQLTypeDefinitionFieldArg>,
     pub resolver_code: TokenStream,
     pub is_type_inferred: bool,
@@ -897,6 +951,43 @@ impl GraphQLTypeDefiniton {
                     quote!(: #_type)
                 };
 
+                let tracing = if cfg!(feature = "traced") {
+                    let span_name = format!("{}.{}", self.name, name);
+                    let span_name = syn::LitStr::new(&span_name, name.span());
+
+                    let args = field.args.iter().filter_map(|arg| {
+                        let name = &arg.name;
+                        let arg_name = syn::Ident::new(name, arg._type.span());
+                        let arg_getter = syn::LitStr::new(name, arg._type.span());
+                        let scalar = &self.scalar.clone().unwrap_or_else(
+                            || syn::parse2(quote!(::juniper::DefaultScalarValue)).unwrap(),
+                        );
+                        let ty = &arg._type;
+                        field.tracing
+                            .as_ref()
+                            .map(|t| t.skip.get(name))
+                            .flatten()
+                            .is_none()
+                            .then(|| {
+                                quote!(
+                                    #arg_name = ::juniper::tracing::field::debug(
+                                        args.get::<#ty>(#arg_getter).unwrap_or_else(|| {
+                                            ::juniper::FromInputValue::<#scalar>::from_implicit_null()
+                                        })
+                                    )
+                                )
+                            })
+                    });
+
+                    let wrapper = quote!(
+                        .instrument(::juniper::tracing::trace_span!(#span_name, #(#args, )*))
+                    );
+
+                    Some(wrapper)
+                } else {
+                    None
+                };
+
                 if field.is_async {
                     quote!(
                         #name => {
@@ -917,7 +1008,7 @@ impl GraphQLTypeDefiniton {
                                     Ok(None) => Ok(::juniper::Value::null()),
                                     Err(e) => Err(e),
                                 }
-                            };
+                            } #tracing;
                             Box::pin(f)
                         },
                     )
@@ -993,6 +1084,12 @@ impl GraphQLTypeDefiniton {
                 None
             };
 
+            let instrument = if cfg!(feature = "traced") {
+                Some(quote!(use ::juniper::tracing::Instrument as _;))
+            } else {
+                None
+            };
+
             quote!(
                 impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty #type_generics_tokens
                     #where_async
@@ -1008,6 +1105,7 @@ impl GraphQLTypeDefiniton {
                     {
                         use ::juniper::futures::future;
                         use ::juniper::GraphQLType;
+                        #instrument;
                         match field {
                             #( #resolve_matches_async )*
                             _ => {

From 3c555ddb0015f4c28747d89356306bcae0009b40 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Wed, 21 Jul 2021 15:13:15 +0300
Subject: [PATCH 53/72] Impl codegen, add initial docs and tests

---
 docs/book/content/tracing/index.md            |  73 ++-
 docs/book/tests/Cargo.toml                    |   2 +-
 examples/tracing_support/Cargo.toml           |   2 +-
 .../juniper_tests/src/explicit_null.rs        |   2 +-
 juniper/Cargo.toml                            |   7 +-
 juniper/src/lib.rs                            |  22 +-
 juniper/src/macros/tracing.rs                 |  10 +-
 juniper/src/parser/tests/value.rs             |   7 +-
 juniper/src/schema/model.rs                   |  11 +-
 juniper/src/tests/fixtures/mod.rs             |   4 +
 juniper/src/tests/fixtures/starwars/schema.rs |   2 +-
 juniper/src/tests/fixtures/tracing/mod.rs     | 529 +++++++++++++++++
 juniper/src/tests/fixtures/tracing/schema.rs  | 532 ++++++++++++++++++
 juniper/src/tests/mod.rs                      |  10 +-
 juniper/src/tests/trace_async_tests.rs        |   1 +
 juniper/src/tests/trace_sync_tests.rs         |   1 +
 juniper/src/tests/trace_tests.rs              | 500 ++++++++++++++--
 juniper/src/util.rs                           |  19 +
 juniper_codegen/Cargo.toml                    |   3 +-
 juniper_codegen/src/common/gen.rs             |   7 +-
 juniper_codegen/src/derive_enum.rs            |   2 +
 juniper_codegen/src/derive_input_object.rs    |   2 +
 juniper_codegen/src/derive_object.rs          |   2 +
 juniper_codegen/src/graphql_interface/attr.rs |   8 +-
 juniper_codegen/src/graphql_interface/mod.rs  |  84 ++-
 .../src/graphql_interface/tracing.rs          |  53 ++
 juniper_codegen/src/impl_object.rs            |   2 +
 juniper_codegen/src/lib.rs                    |   2 +-
 juniper_codegen/src/util/mod.rs               | 177 +++---
 juniper_codegen/src/util/tracing.rs           | 338 +++++++++++
 30 files changed, 2230 insertions(+), 184 deletions(-)
 create mode 100644 juniper/src/tests/fixtures/tracing/mod.rs
 create mode 100644 juniper/src/tests/fixtures/tracing/schema.rs
 create mode 100644 juniper/src/tests/trace_async_tests.rs
 create mode 100644 juniper/src/tests/trace_sync_tests.rs
 create mode 100644 juniper_codegen/src/graphql_interface/tracing.rs
 create mode 100644 juniper_codegen/src/util/tracing.rs

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 8fbd820ce..3d078e337 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -1,14 +1,17 @@
 # Tracing
 
-Juniper has optional support for the [tracing](https://crates.io/crates/tracing) crate for instrumentation.
+Juniper has optional support for the [tracing] crate for instrumentation.
 
-This feature is off by default and can be enabled via the `tracing` feature.
+This feature is off by default and can be enabled via the `trace-sync`
+feature to enable tracing of a sync code, `trace-async` feature to enable
+tracing of an async code and subscriptions, and `trace` to enable 
+functionality of both `trace-sync`and `trace-async` features.
 
 !FILENAME Cargo.toml
 
 ```toml
 [dependencies]
-juniper = { version = "0.14.2", features = ["default", "traced"]}
+juniper = { version = "0.14.7", features = ["default", "trace"]}
 tracing = "0.1.17"
 tracing-subscriber = "0.2.9"
 ```
@@ -64,3 +67,67 @@ async fn main() {
         .unwrap();
 }
 ```
+
+To trace how GraphQL object being resolved you need to enable one of tracing 
+features and use `tracing` argument on top-level `#[graphql_object]` and
+`#[graphql_interface]` attributes. `tracing` argument can be used with one of
+provided arguments:
+`"trace-sync"`, `"trace-async"`, `"trace-all"` and `"complex"`.
+ - Use `trace-sync` to trace only synchronous part (struct fields and `fn`s).
+ - Use `trace-async` to trace only asynchronous part (`async fn`s) and
+subscriptions.
+ - Use `complex` to trace only fields marked with `#[tracing(complex)]`
+ - Use `skip-all` to skip all fields.
+
+In addition you can use `#[tracing(no_trace)]` with all variants above to
+exclude field from tracing even if it belongs to traced group.
+
+**Note: `trace-sync` feature is mutually exclusive with `"trace-async"`
+argument and vice versa.**
+
+If resolving of certain field requires additional arguments (when used `fn`s or
+`async fn`s) they also will be included in resulted trace (except `self` and
+`Context`). You can use `skip` argument of `#[trace]` attribute, to skip some
+arguments, similarly to the [`skip`] for `#[instrument]`
+
+```rust
+struct Catalog;
+
+#[graphql_object]
+impl Catalog {
+    async fn products(filter: Filter, count: i32) -> Vec<Product> {
+        // Some query
+    }
+}
+
+struct User {
+    id: i64
+}
+
+#[graphql_object]
+impl User {
+    fn id(&self) -> i64 {
+        self.id
+    }
+
+    async fn friends(&self) -> Vec<i64> {
+        // async database query 
+    }
+}
+```
+
+In case above both `filter` and `count` will be recorded in [`Span`] for
+`Catalog::products(...)`.
+
+
+## `#[tracing]` attribute
+
+In most cases `#[tracing]` mimics behaviour of the `#[instrument]` attribute
+from [tracing] crate and you could use it as a reference. With the only key
+deference you should understand, it applied implicitly to all sync resolvers
+(including the derived ones) when the `trace-sync` feature is enabled, to
+all async resolvers when `trace-async` feature is enabled and to all if the
+`trace` feature is enabled.
+
+[tracing]: https://crates.io/crates/tracing
+[`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 4ad9c2882..687a94ed3 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Magnus Hallin <mhallin@fastmail.com>"]
 build = "build.rs"
 
 [dependencies]
-juniper = { path = "../../../juniper", features = ["default", "traced"] }
+juniper = { path = "../../../juniper", features = ["default", "trace"] }
 juniper_iron = { path = "../../../juniper_iron" }
 juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index 7a36370ae..8afe8eb4d 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -10,5 +10,5 @@ tracing = "0.1.21"
 tracing-subscriber = "0.2.12"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
-juniper = { path = "../../juniper", features = ["traced"] }
+juniper = { path = "../../juniper", features = ["trace"] }
 tokio = { version = "0.2.22", features = ["rt-core", "macros", "blocking"] }
diff --git a/integration_tests/juniper_tests/src/explicit_null.rs b/integration_tests/juniper_tests/src/explicit_null.rs
index 3ade4b528..70de5a1e7 100644
--- a/integration_tests/juniper_tests/src/explicit_null.rs
+++ b/integration_tests/juniper_tests/src/explicit_null.rs
@@ -6,7 +6,7 @@ impl juniper::Context for Context {}
 
 pub struct Query;
 
-#[derive(juniper::GraphQLInputObject)]
+#[derive(Debug, juniper::GraphQLInputObject)]
 struct ObjectInput {
     field: Nullable<i32>,
 }
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index ebd27c51e..a7a54d8c8 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -30,7 +30,9 @@ expose-test-schema = ["anyhow", "serde_json"]
 graphql-parser-integration = ["graphql-parser"]
 scalar-naivetime = []
 schema-language = ["graphql-parser-integration"]
-traced = ["tracing", "juniper_codegen/traced"]
+trace = ["tracing", "tracing-futures", "juniper_codegen/trace-sync", "juniper_codegen/trace-async"]
+trace-async = ["tracing", "tracing-futures", "juniper_codegen/trace-async"]
+trace-sync = ["tracing", "tracing-futures", "juniper_codegen/trace-sync"]
 
 [dependencies]
 juniper_codegen = { path = "../juniper_codegen" }
@@ -53,6 +55,9 @@ url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
 tracing = { version = "0.1.23", optional = true }
+tracing-futures = { version = "0.2.5", optional = true, features = ["futures-03"] }
+tracing-core = "0.1"
+tracing-subscriber = "0.2"
 
 [dev-dependencies]
 bencher = "0.1.2"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 5a241b7e4..5cb790c28 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -106,10 +106,13 @@ pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::{BoxFuture, LocalBoxFuture};
 
-// This is required by the `traced` feature.
-#[cfg(feature = "traced")]
+// This is required by the `trace` feature.
+#[cfg(feature = "tracing")]
 #[doc(hidden)]
 pub use tracing;
+#[cfg(feature = "tracing-futures")]
+#[doc(hidden)]
+pub use tracing_futures;
 
 // Depend on juniper_codegen and re-export everything in it.
 // This allows users to just depend on juniper and get the derive
@@ -146,6 +149,8 @@ mod executor_tests;
 
 // Needs to be public because macros use it.
 pub use crate::util::to_camel_case;
+#[cfg(feature = "tracing-futures")]
+pub use crate::util::tracing::InstrumentInternal;
 
 use crate::{
     executor::{execute_validated_query, get_operation},
@@ -242,7 +247,6 @@ where
 {
     __juniper_span_trace!("execute_sync");
 
-    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -297,7 +301,6 @@ where
 {
     __juniper_span_trace!("execute");
 
-    __juniper_trace!("document: {}", document_source);
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
@@ -353,10 +356,13 @@ where
     SubscriptionT::TypeInfo: Sync,
     S: ScalarValue + Send + Sync,
 {
+    __juniper_span_trace!("resolve_into_stream");
+
     let document: crate::ast::OwnedDocument<'a, S> =
         parse_document_source(document_source, &root_node.schema)?;
 
     {
+        __juniper_span_trace!("rule_validation");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -372,6 +378,7 @@ where
     let operation = get_operation(&document, operation_name)?;
 
     {
+        __juniper_span_trace!("validate_input_values");
         let errors = validate_input_values(&variables, operation, &root_node.schema);
 
         if !errors.is_empty() {
@@ -382,8 +389,11 @@ where
         }
     }
 
-    executor::resolve_validated_subscription(&document, operation, root_node, variables, context)
-        .await
+    let f = executor::resolve_validated_subscription(
+        &document, operation, root_node, variables, context,
+    );
+
+    __juniper_instrument_trace!(f, "resolve_validated_subscription").await
 }
 
 /// Execute the reference introspection query in the provided schema
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 0cffa63ff..05882e398 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -4,11 +4,11 @@
 #[macro_export]
 macro_rules! __juniper_instrument_internal {
     ($trace_type:ident; $fut:expr, $($element:expr),*) => {{
-        #[cfg(feature = "traced")]
+        #[cfg(feature = "tracing")]
         {
             $crate::tracing::Instrument::instrument($fut, tracing::$trace_type!($($element),*))
         }
-        #[cfg(not(feature = "traced"))]
+        #[cfg(not(feature = "tracing"))]
         {
             $fut
         }
@@ -29,9 +29,9 @@ macro_rules! __juniper_instrument_trace {
 #[macro_export]
 macro_rules! __juniper_span_internal {
     ($trace_type:ident; $($element:expr),*) => {
-        #[cfg(feature = "traced")]
+        #[cfg(feature = "tracing")]
         let myspan = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
-        #[cfg(feature = "traced")]
+        #[cfg(feature = "tracing")]
         let _enter = myspan.enter();
     };
 }
@@ -82,7 +82,7 @@ macro_rules! __juniper_span_trace_error {
 #[macro_export]
 macro_rules! __juniper_trace_internal {
     ($trace_type:ident; $($element:expr),*) => {{
-        #[cfg(feature = "traced")]
+        #[cfg(feature = "tracing")]
         {
             $crate::tracing::$trace_type!($($element),*);
         }
diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs
index fe425cd1e..6e200c717 100644
--- a/juniper/src/parser/tests/value.rs
+++ b/juniper/src/parser/tests/value.rs
@@ -12,22 +12,23 @@ use crate::{
     GraphQLEnum, GraphQLInputObject,
 };
 
-#[derive(GraphQLEnum)]
+#[derive(Debug, GraphQLEnum)]
 enum Enum {
     EnumValue,
 }
 
-#[derive(GraphQLInputObject)]
+#[derive(Debug, GraphQLInputObject)]
 struct Bar {
     foo: String,
 }
 
-#[derive(GraphQLInputObject)]
+#[derive(Debug, GraphQLInputObject)]
 struct Foo {
     key: i32,
     other: Bar,
 }
 
+#[derive(Debug)]
 struct Query;
 
 #[crate::graphql_object(Scalar = S)]
diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs
index 09d4c85e0..8bb143b60 100644
--- a/juniper/src/schema/model.rs
+++ b/juniper/src/schema/model.rs
@@ -605,29 +605,30 @@ mod test {
 
         #[test]
         fn schema_language() {
-            #[derive(GraphQLObject, Default)]
+            #[derive(Debug, GraphQLObject, Default)]
             struct Cake {
                 fresh: bool,
             }
-            #[derive(GraphQLObject, Default)]
+            #[derive(Debug, GraphQLObject, Default)]
             struct IceCream {
                 cold: bool,
             }
-            #[derive(GraphQLUnion)]
+            #[derive(Debug, GraphQLUnion)]
             enum GlutenFree {
                 Cake(Cake),
                 IceCream(IceCream),
             }
-            #[derive(GraphQLEnum)]
+            #[derive(Debug, GraphQLEnum)]
             enum Fruit {
                 Apple,
                 Orange,
             }
-            #[derive(GraphQLInputObject)]
+            #[derive(Debug, GraphQLInputObject)]
             struct Coordinate {
                 latitude: f64,
                 longitude: f64,
             }
+            #[derive(Debug)]
             struct Query;
             #[graphql_object]
             impl Query {
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index 71b5bec84..799b6fed2 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -2,3 +2,7 @@
 
 /// GraphQL schema and data from Star Wars.
 pub mod starwars;
+
+#[cfg(any(feature = "trace", feature = "trace-sync", feature = "trace-async"))]
+/// Fixtures used to test integration with `tracing` crate.
+pub mod tracing;
diff --git a/juniper/src/tests/fixtures/starwars/schema.rs b/juniper/src/tests/fixtures/starwars/schema.rs
index 4b84568cb..89bfce9ad 100644
--- a/juniper/src/tests/fixtures/starwars/schema.rs
+++ b/juniper/src/tests/fixtures/starwars/schema.rs
@@ -67,7 +67,7 @@ pub trait Character {
     fn friends_ids(&self) -> &[String];
 }
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct Human {
     id: String,
     name: String,
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
new file mode 100644
index 000000000..315e440c4
--- /dev/null
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -0,0 +1,529 @@
+#![allow(missing_docs)]
+
+pub mod schema;
+
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
+
+use tracing::{
+    field::{Field, Visit},
+    span::{Attributes, Record},
+    Event, Level, Metadata,
+};
+use tracing_core::{span, Subscriber};
+
+#[derive(Clone, Debug)]
+pub struct TestSpan {
+    id: span::Id,
+    fields: HashMap<String, String>,
+    metadata: &'static Metadata<'static>,
+}
+
+#[derive(Clone, Debug)]
+pub struct TestEvent {
+    fields: HashMap<String, String>,
+    metadata: &'static Metadata<'static>,
+}
+
+#[derive(Clone, Debug)]
+pub enum SubscriberEvent {
+    NewSpan(TestSpan),
+    Enter(span::Id),
+    Exit(span::Id),
+    CloneSpan(span::Id),
+    TryClose(span::Id),
+    Event(TestEvent),
+}
+
+impl TestEvent {
+    pub fn new(ev: &Event<'_>) -> Self {
+        let mut visitor = Visitor::new();
+
+        ev.record(&mut visitor);
+        Self {
+            fields: visitor.0,
+            metadata: ev.metadata(),
+        }
+    }
+}
+
+struct Visitor(HashMap<String, String>);
+
+impl Visitor {
+    fn new() -> Self {
+        Self(HashMap::new())
+    }
+}
+
+impl Visit for Visitor {
+    fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
+        self.0
+            .insert(field.name().to_owned(), format!("{:?}", value));
+    }
+}
+
+#[derive(Clone)]
+pub struct TestSubscriber {
+    counter: Rc<RefCell<u64>>,
+    events: Rc<RefCell<Vec<SubscriberEvent>>>,
+}
+
+unsafe impl Sync for TestSubscriber {}
+
+unsafe impl Send for TestSubscriber {}
+
+impl TestSubscriber {
+    pub fn new() -> Self {
+        TestSubscriber {
+            counter: Rc::new(RefCell::new(1)),
+            events: Rc::new(RefCell::new(Vec::new())),
+        }
+    }
+
+    pub fn assert(self) -> SubscriberAssert {
+        SubscriberAssert {
+            name_to_span: HashMap::new(),
+            events: self.events.borrow().clone(),
+        }
+    }
+}
+
+impl Subscriber for TestSubscriber {
+    fn enabled(&self, _: &Metadata<'_>) -> bool {
+        true
+    }
+
+    fn new_span(&self, attrs: &Attributes<'_>) -> span::Id {
+        let id = *self.counter.borrow();
+        *self.counter.borrow_mut() = id + 1;
+
+        let mut visitor = Visitor::new();
+        attrs.record(&mut visitor);
+
+        let id = span::Id::from_u64(id);
+        let test_span = TestSpan {
+            id: id.clone(),
+            metadata: attrs.metadata(),
+            fields: visitor.0,
+        };
+        self.events
+            .borrow_mut()
+            .push(SubscriberEvent::NewSpan(test_span));
+        id
+    }
+
+    fn record(&self, _: &span::Id, _: &Record<'_>) {}
+
+    fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
+
+    fn event(&self, event: &Event<'_>) {
+        self.events
+            .borrow_mut()
+            .push(SubscriberEvent::Event(TestEvent::new(event)))
+    }
+
+    fn enter(&self, id: &span::Id) {
+        self.events
+            .borrow_mut()
+            .push(SubscriberEvent::Enter(id.clone()))
+    }
+
+    fn exit(&self, id: &span::Id) {
+        self.events
+            .borrow_mut()
+            .push(SubscriberEvent::Exit(id.clone()))
+    }
+
+    fn clone_span(&self, id: &span::Id) -> span::Id {
+        self.events
+            .borrow_mut()
+            .push(SubscriberEvent::CloneSpan(id.clone()));
+        id.clone()
+    }
+
+    fn try_close(&self, id: span::Id) -> bool {
+        self.events.borrow_mut().push(SubscriberEvent::TryClose(id));
+        false
+    }
+}
+
+pub struct SubscriberAssert {
+    name_to_span: HashMap<span::Id, String>,
+    pub events: Vec<SubscriberEvent>,
+}
+
+impl SubscriberAssert {
+    /// Checks whether next tracing event is creation of a new span with the
+    /// given name and fields.
+    pub fn new_span<S: AsSpan + ?Sized>(mut self, expected: &S) -> Self {
+        let current_event = self.events.remove(0);
+        match current_event {
+            SubscriberEvent::NewSpan(span) => {
+                assert_eq!(expected.name(), span.metadata.name());
+                if expected.is_strict() {
+                    assert_eq!(
+                        expected.fields().len(),
+                        span.fields.len(),
+                        "Fields count doesn't match, expected: {:?}, actual: {:?}",
+                        expected.fields(),
+                        span.fields,
+                    )
+                }
+                expected.fields().into_iter().for_each(|(f_name, val)| {
+                    assert_eq!(
+                        Some(&val),
+                        span.fields.get(&f_name),
+                        "Field {} in span {} either doesn't exist or has value
+                         different from {}, values: {:?}",
+                        f_name,
+                        expected.name(),
+                        val,
+                        span.fields,
+                    );
+                });
+                if let Some(level) = expected.level() {
+                    assert_eq!(
+                        &level,
+                        span.metadata.level(),
+                        "Expected level: '{}' in span: {:?}",
+                        level,
+                        span,
+                    )
+                }
+                if let Some(target) = expected.target() {
+                    assert_eq!(
+                        target,
+                        span.metadata.target(),
+                        "Expected target: '{}' in span: {:?}",
+                        target,
+                        span,
+                    )
+                }
+
+                self.name_to_span
+                    .insert(span.id, span.metadata.name().to_owned());
+            }
+            ev => assert!(
+                false,
+                "Expected `NewSpan`: {}, got {:?}, remaining events: {:?}",
+                expected.name(),
+                ev,
+                self.events,
+            ),
+        }
+        self
+    }
+
+    /// Checks whether the next step is entering the span with the given name.
+    pub fn enter<S: AsSpan + ?Sized>(mut self, span: &S) -> Self {
+        let current_event = self.events.remove(0);
+        match current_event {
+            SubscriberEvent::Enter(id) => match self.name_to_span.get(&id) {
+                None => assert!(
+                    false,
+                    "No span with id: {:?}, registered spans: {:?}",
+                    id, self.name_to_span,
+                ),
+                Some(actual_name) => assert_eq!(
+                    span.name(),
+                    actual_name.as_str(),
+                    "Entered span with name: {}, expected: {}",
+                    actual_name,
+                    span.name(),
+                ),
+            },
+            ev => assert!(
+                false,
+                "Expected `Enter`: {}, got: {:?}, remaining events: {:?}",
+                span.name(),
+                ev,
+                self.events,
+            ),
+        }
+        self.re_enter(span)
+    }
+
+    /// Checks whether the next step is exiting the span with the given name.
+    pub fn exit<S: AsSpan + ?Sized>(mut self, span: &S) -> Self {
+        let current_event = self.events.remove(0);
+        match current_event {
+            SubscriberEvent::Exit(id) => match self.name_to_span.get(&id) {
+                None => assert!(
+                    false,
+                    "No span with id: {:?}, registered spans: {:?}",
+                    id, self.name_to_span,
+                ),
+                Some(actual_name) => assert_eq!(
+                    span.name(),
+                    actual_name.as_str(),
+                    "Exited span with name: {}, expected: {}",
+                    actual_name,
+                    span.name(),
+                ),
+            },
+            ev => assert!(
+                false,
+                "Expected `Exit`: {}, got: {:?}, remaining events: {:?}",
+                span.name(),
+                ev,
+                self.events,
+            ),
+        }
+        self
+    }
+
+    /// Checks whether the next step is attempt to close span with the given
+    /// name.
+    pub fn try_close<S: AsSpan + ?Sized>(mut self, span: &S) -> Self {
+        let current_event = self.events.remove(0);
+        match current_event {
+            SubscriberEvent::TryClose(id) => match self.name_to_span.get(&id) {
+                None => assert!(
+                    false,
+                    "No span with id: {:?}, registered spans: {:?}",
+                    id, self.name_to_span,
+                ),
+                Some(actual_name) => assert_eq!(
+                    span.name(),
+                    actual_name.as_str(),
+                    "Attempted to close span with name: {}, expected: {}",
+                    actual_name,
+                    span.name(),
+                ),
+            },
+            ev => assert!(
+                false,
+                "Expected `TryClose`: {}, got: {:?}, remaining events: {:?}",
+                span.name(),
+                ev,
+                self.events,
+            ),
+        }
+        self
+    }
+
+    /// Checks whether next step is event with the given level, optionally
+    /// target and fields.
+    pub fn event(mut self, level: Level, target: Option<&str>, fields: Vec<(&str, &str)>) -> Self {
+        let current_event = self.events.remove(0);
+        match current_event {
+            SubscriberEvent::Event(ev) => {
+                assert_eq!(ev.metadata.level(), &level);
+                if let Some(target) = target {
+                    assert_eq!(ev.metadata.target(), target);
+                }
+                for (name, value) in fields {
+                    assert_eq!(ev.fields.get(name).map(String::as_str), Some(value))
+                }
+            }
+            ev => assert!(
+                false,
+                "Expected `Event`, got: {:?}, remaining events: {:?}",
+                ev, self.events,
+            ),
+        }
+        self
+    }
+
+    /// Checks whether next steps are `new_span` then `enter` then `exit` and
+    /// finally `try_close`.
+    pub fn simple_span<S: AsSpan + ?Sized>(self, span: &S) -> Self {
+        self.new_span(span)
+            .enter(span)
+            .exit(span)
+            .re_enter(span)
+            .try_close(span)
+    }
+
+    /// Checks whether next to steps is creation of a new span with the given
+    /// name and entering it.
+    pub fn enter_new_span<S: AsSpan + ?Sized>(self, span: &S) -> Self {
+        self.new_span(span).enter(span).re_enter(span)
+    }
+
+    /// Checks whether next two steps is exiting the span with the given name
+    /// and attempt to close it.
+    pub fn close_exited<S: AsSpan + ?Sized>(self, span: &S) -> Self {
+        self.exit(span).re_enter(span).try_close(span)
+    }
+
+    /// Checks whether next two steps is exiting and re-entering the same span
+    /// with the given name.
+    ///
+    /// This may be useful in case of tracing when sync object is resolved in
+    /// async context.
+    pub fn re_enter<S: AsSpan + ?Sized>(self, span: &S) -> Self {
+        use SubscriberEvent as Ev;
+
+        let first = self.events.get(0);
+        let second = self.events.get(1);
+        match (first, second) {
+            (Some(Ev::Exit(first)), Some(Ev::Enter(second))) if first == second => {
+                let next_span = self
+                    .name_to_span
+                    .get(first)
+                    .unwrap_or_else(|| panic!("No span with id '{:?}'", first));
+                if next_span.as_str() == span.name() {
+                    return self.exit(span).enter(span);
+                }
+            }
+            (Some(Ev::Enter(first)), Some(Ev::Exit(second))) if first == second => {
+                let next_span = self
+                    .name_to_span
+                    .get(first)
+                    .unwrap_or_else(|| panic!("No span with id '{:?}'", first));
+                if next_span.as_str() == span.name() {
+                    return self.enter(span).exit(span);
+                }
+            }
+            _ => {}
+        }
+        self
+    }
+}
+
+pub struct SpanLike {
+    name: String,
+    level: Option<Level>,
+    target: Option<String>,
+    fields: Vec<(String, String)>,
+    strict_fields: bool,
+}
+
+pub trait AsSpan {
+    fn name(&self) -> &str;
+
+    fn level(&self) -> Option<Level> {
+        None
+    }
+
+    fn target(&self) -> Option<&str> {
+        None
+    }
+
+    fn fields(&self) -> Vec<(String, String)> {
+        vec![]
+    }
+
+    fn is_strict(&self) -> bool {
+        false
+    }
+}
+
+impl AsSpan for str {
+    fn name(&self) -> &str {
+        self
+    }
+}
+
+impl AsSpan for SpanLike {
+    fn name(&self) -> &str {
+        self.name.as_str()
+    }
+
+    fn level(&self) -> Option<Level> {
+        self.level
+    }
+
+    fn target(&self) -> Option<&str> {
+        self.target.as_ref().map(String::as_str)
+    }
+
+    fn fields(&self) -> Vec<(String, String)> {
+        self.fields.clone()
+    }
+
+    fn is_strict(&self) -> bool {
+        self.strict_fields
+    }
+}
+
+pub trait SpanExt {
+    fn with_name(self, name: &str) -> SpanLike;
+
+    fn with_level(self, level: Level) -> SpanLike;
+
+    fn with_target(self, target: &str) -> SpanLike;
+
+    fn with_field(self, field: &str, value: &str) -> SpanLike;
+
+    fn with_strict_fields(self, strict: bool) -> SpanLike;
+}
+
+impl SpanExt for &str {
+    fn with_name(self, name: &str) -> SpanLike {
+        SpanLike {
+            name: name.to_owned(),
+            level: None,
+            target: None,
+            fields: vec![],
+            strict_fields: false,
+        }
+    }
+
+    fn with_level(self, level: Level) -> SpanLike {
+        SpanLike {
+            name: self.to_owned(),
+            level: Some(level),
+            target: None,
+            fields: vec![],
+            strict_fields: false,
+        }
+    }
+
+    fn with_target(self, target: &str) -> SpanLike {
+        SpanLike {
+            name: self.to_owned(),
+            level: None,
+            target: Some(target.to_owned()),
+            fields: vec![],
+            strict_fields: false,
+        }
+    }
+
+    fn with_field(self, field: &str, value: &str) -> SpanLike {
+        SpanLike {
+            name: self.to_owned(),
+            level: None,
+            target: None,
+            fields: vec![(field.to_owned(), value.to_owned())],
+            strict_fields: false,
+        }
+    }
+
+    fn with_strict_fields(self, strict: bool) -> SpanLike {
+        SpanLike {
+            name: self.to_owned(),
+            level: None,
+            target: None,
+            fields: vec![],
+            strict_fields: strict,
+        }
+    }
+}
+
+impl SpanExt for SpanLike {
+    fn with_name(mut self, name: &str) -> SpanLike {
+        self.name = name.to_owned();
+        self
+    }
+
+    fn with_level(mut self, level: Level) -> SpanLike {
+        self.level = Some(level);
+        self
+    }
+
+    fn with_target(mut self, target: &str) -> SpanLike {
+        self.target = Some(target.to_owned());
+        self
+    }
+
+    fn with_field(mut self, field: &str, value: &str) -> SpanLike {
+        self.fields.push((field.to_owned(), value.to_owned()));
+        self
+    }
+
+    fn with_strict_fields(mut self, strict: bool) -> SpanLike {
+        self.strict_fields = strict;
+        self
+    }
+}
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
new file mode 100644
index 000000000..52f1fabba
--- /dev/null
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -0,0 +1,532 @@
+#![allow(missing_docs)]
+
+use std::collections::HashMap;
+
+use tracing::instrument;
+
+use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLObject};
+
+#[derive(Debug)]
+pub struct Database {
+    inner: HashMap<i32, String>,
+}
+
+impl Database {
+    pub fn new() -> Self {
+        let mut inner = HashMap::new();
+        inner.insert(42, "Meaning of life".to_owned());
+        Self { inner }
+    }
+
+    /// Query mock, instrumented by `tracing` crate.
+    #[instrument(skip(self))]
+    pub fn traced_query(&self, id: i32) -> Option<String> {
+        self.inner.get(&id).cloned()
+    }
+
+    /// Non traced query mock.
+    pub fn non_traced_query(&self, id: i32) -> Option<String> {
+        self.inner.get(&id).cloned()
+    }
+}
+
+impl Context for Database {}
+
+/// Query root with various queries used to test tracing compatibility.
+pub struct Query;
+
+#[graphql_object(context = Database)]
+impl Query {
+    /// Simple sync query with no arguments.
+    fn foo() -> Foo {
+        Foo { id: 37 }
+    }
+
+    /// Sync query with argument.
+    fn bar(id: i32) -> Bar {
+        Bar { id }
+    }
+
+    /// Simple async query.
+    async fn async_foo() -> DerivedFoo {
+        DerivedFoo {
+            id: 42,
+            non_traced: "None can trace this".to_owned(),
+            target: false,
+            level: false,
+        }
+    }
+
+    /// Async query with argument.
+    async fn async_bar(id: i32) -> Bar {
+        Bar { id }
+    }
+
+    /// Query that returns `Foo` wrapped in `FooBar`.
+    fn foo_bar() -> FooBarValue {
+        FooBarValue::Foo(Foo { id: 1 })
+    }
+
+    /// Query that returns collection of objects wrapped into GraphQL interface.
+    fn foo_bars(id: i32) -> Vec<FooBarValue> {
+        vec![
+            FooBarValue::Foo(Foo { id }),
+            FooBarValue::Bar(Bar { id }),
+            FooBarValue::DerivedFoo(DerivedFoo {
+                id,
+                non_traced: "leave no traces".to_owned(),
+                target: false,
+                level: false,
+            }),
+        ]
+    }
+
+    /// Returns GraphQL object marked with `trace = "trace-async"`.
+    async fn trace_async() -> TraceAsync {
+        TraceAsync
+    }
+
+    /// Returns derived GraphQL object marked with `trace = "trace-async"`.
+    async fn derived_async() -> AsyncDerived {
+        AsyncDerived::default()
+    }
+
+    /// Returns GraphQL object marked with `trace = "trace-sync"`.
+    fn trace_sync() -> TraceSync {
+        TraceSync
+    }
+
+    /// Returns derived GraphQL object marked with `trace = "trace-sync"`.
+    fn derived_sync() -> SyncDerived {
+        SyncDerived::default()
+    }
+
+    /// Returns GraphQL object marked with `trace = "skip-all"`.
+    fn skip_all() -> SkipAll {
+        SkipAll
+    }
+
+    /// Returns derived GraphQL object marked with `trace = "skip-all"`.
+    fn skip_all_derived() -> SkipAllDerived {
+        SkipAllDerived::default()
+    }
+
+    /// Returns GraphQL object marked with `trace = "complex"` in sync manner.
+    fn complex_sync() -> Complex {
+        Complex
+    }
+
+    /// Returns GraphQL object marked with `trace = "complex"` in async manner.
+    async fn complex_async() -> Complex {
+        Complex
+    }
+
+    /// Returns derived GraphQL object marked with `trace = "complex"`.
+    fn complex_derived() -> DerivedComplex {
+        DerivedComplex {
+            complex: false,
+            another_complex: false,
+            sync: 0,
+        }
+    }
+
+    /// Returns GraphQL object wrapped in `InterfacedSimple` GraphQL interface.
+    fn erased_simple() -> InterfacedSimpleValue {
+        InterfacedSimpleValue::TraceSync(TraceSync)
+    }
+
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "trace-sync"`.
+    fn erased_sync() -> InterfacedSyncValue {
+        InterfacedSyncValue::TraceSync(TraceSync)
+    }
+
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "trace-async"`.
+    fn erased_async() -> InterfacedAsyncValue {
+        InterfacedAsyncValue::TraceAsync(TraceAsync)
+    }
+
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "skip-all"`.
+    fn erased_skip_all() -> InterfacedSkipAllValue {
+        InterfacedSkipAllValue::SkipAll(SkipAll)
+    }
+
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "complex"`.
+    fn erased_complex() -> InterfacedComplexValue {
+        InterfacedComplexValue::Complex(Complex)
+    }
+}
+
+/// Simple GraphQL object.
+pub struct Foo {
+    id: i32,
+}
+
+#[graphql_object(context = Database, impl = FooBarValue)]
+impl Foo {
+    /// Sync field calculated from `self`.
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    /// Sync field marked with `no_trace`.
+    #[tracing(no_trace)]
+    fn non_traced(&self) -> &str {
+        "None can trace this"
+    }
+
+    /// Async field marked with `no_trace`.
+    #[tracing(no_trace)]
+    async fn async_non_traced(&self) -> &str {
+        "None can trace this"
+    }
+
+    /// Field with multiple arguments, one of which is skipped.
+    #[tracing(skip(name))]
+    fn skip_argument(&self, name: String, meaning_of_life: i32) -> i32 {
+        let _ = name;
+        meaning_of_life
+    }
+
+    /// Field with it's `target` overwritten.
+    #[tracing(target = "my_target")]
+    fn target(&self) -> bool {
+        true
+    }
+
+    /// Field with it's `level` overwritten.
+    #[tracing(level = "warn")]
+    fn level(&self) -> bool {
+        true
+    }
+}
+
+#[graphql_interface(async)]
+impl FooBar for Foo {
+    fn is_foo(&self) -> bool {
+        true
+    }
+
+    async fn is_bar(&self) -> bool {
+        false
+    }
+}
+
+/// Simple GraphQL object with more advanced field resolvers.
+pub struct Bar {
+    id: i32,
+}
+
+#[graphql_object(context = Database, impl = FooBarValue)]
+impl Bar {
+    /// Custom field.
+    #[tracing(fields(self.id = self.id))]
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    /// Field that has signature identical to `FooBar`s one but in fact traced.
+    fn non_traced(&self) -> bool {
+        false
+    }
+
+    /// Field with default arguments.
+    #[graphql(arguments(this(default = 42), another(default = 0), skipped(default = 1),))]
+    #[tracing(skip(skipped))]
+    fn default_arg(&self, this: i32, another: i32, skipped: i32) -> i32 {
+        this + another + skipped
+    }
+
+    /// Traced database query.
+    async fn traced_data(&self, context: &Database) -> Option<String> {
+        context.traced_query(self.id)
+    }
+
+    /// Non traced database query.
+    async fn non_traced_data(&self, context: &Database) -> Option<String> {
+        context.non_traced_query(self.id)
+    }
+}
+
+#[graphql_interface(async)]
+impl FooBar for Bar {
+    fn is_foo(&self) -> bool {
+        false
+    }
+
+    async fn is_bar(&self) -> bool {
+        true
+    }
+}
+
+/// Derived `GraphQLObject`.
+#[derive(GraphQLObject)]
+#[graphql(impl = FooBarValue, context = Database)]
+pub struct DerivedFoo {
+    /// Resolver from that has context bound and const bound trace fields.
+    #[tracing(fields(self.id = self.id, custom_fields = "work"))]
+    id: i32,
+
+    /// Field marked with `no_trace` within derived GraphQLObject.
+    #[tracing(no_trace)]
+    non_traced: String,
+
+    /// Field with it's `target` overwritten.
+    #[tracing(target = "my_target")]
+    target: bool,
+
+    /// Field with it's `level` overwritten.
+    #[tracing(level = "warn")]
+    level: bool,
+}
+
+#[graphql_interface(async)]
+impl FooBar for DerivedFoo {
+    fn is_foo(&self) -> bool {
+        true
+    }
+
+    async fn is_bar(&self) -> bool {
+        false
+    }
+}
+
+/// GraphQL interface with various tracing features.
+#[graphql_interface(for = [DerivedFoo, Foo, Bar], context = Database, async)]
+pub trait FooBar {
+    /// Simple sync field.
+    fn is_foo(&self) -> bool;
+
+    /// Simple async field.
+    async fn is_bar(&self) -> bool;
+
+    /// Interface field marked with `no_trace`.
+    #[tracing(no_trace)]
+    fn non_traced(&self) -> bool {
+        true
+    }
+
+    /// Async interface field marked with `no_trace`.
+    #[tracing(no_trace)]
+    async fn async_non_traced(&self) -> bool {
+        true
+    }
+
+    /// Interface field with various arguments.
+    #[tracing(skip(skipped))]
+    fn with_arg(
+        &self,
+        id: i32,
+        skipped: i32,
+        #[graphql(default = 0)] default: i32,
+        #[graphql(default = 1)] overwritten: i32,
+    ) -> i32 {
+        id + skipped + default + overwritten
+    }
+
+    /// Async interface field with various arguments.
+    #[tracing(skip(skipped))]
+    async fn async_with_arg(
+        &self,
+        id: i32,
+        skipped: i32,
+        #[graphql(default = 0)] default: i32,
+        #[graphql(default = 1)] overwritten: i32,
+    ) -> i32 {
+        id + skipped + default + overwritten
+    }
+
+    #[tracing(target = "my_target")]
+    fn target(&self) -> i32 {
+        1
+    }
+
+    #[tracing(level = "warn")]
+    fn level(&self) -> i32 {
+        2
+    }
+}
+
+/// GraphQL object marked with `trace = "skip-sync"`
+pub struct TraceSync;
+
+#[graphql_object(
+    trace = "trace-sync",
+    impl = [InterfacedSimpleValue, InterfacedSyncValue],
+)]
+impl TraceSync {
+    pub fn sync_fn(&self) -> i32 {
+        1
+    }
+
+    pub async fn async_fn(&self) -> i32 {
+        2
+    }
+}
+
+/// Derived GraphQL object marked with `trace = "trace-sync"`.
+#[derive(Default, GraphQLObject)]
+#[graphql(trace = "trace-sync")]
+pub struct SyncDerived {
+    /// Simple field
+    sync: i32,
+}
+
+/// GraphQL object marked with `trace = "async"`.
+pub struct TraceAsync;
+
+#[graphql_object(
+    trace = "trace-async",
+    impl = [InterfacedAsyncValue],
+)]
+impl TraceAsync {
+    pub fn sync_fn(&self) -> i32 {
+        1
+    }
+
+    pub async fn async_fn(&self) -> i32 {
+        2
+    }
+}
+
+/// Derived GraphQL object
+#[derive(Default, GraphQLObject)]
+#[graphql(trace = "trace-async")]
+pub struct AsyncDerived {
+    /// Simple field
+    sync: i32,
+}
+
+/// GraphQL object marked with `trace = "skip-all"`.
+pub struct SkipAll;
+
+#[graphql_object(
+    trace = "skip-all",
+    impl = [InterfacedSkipAllValue],
+)]
+impl SkipAll {
+    pub fn sync_fn(&self) -> i32 {
+        1
+    }
+
+    pub async fn async_fn(&self) -> i32 {
+        2
+    }
+}
+
+#[derive(Default, GraphQLObject)]
+#[graphql(trace = "skip-all")]
+pub struct SkipAllDerived {
+    /// Simple field
+    sync: i32,
+}
+
+/// GraphQL object marked with `trace = "complex"`
+pub struct Complex;
+
+#[graphql_object(
+    trace = "complex",
+    impl = [InterfacedComplexValue],
+)]
+impl Complex {
+    #[tracing(complex)]
+    pub fn sync_fn(&self) -> i32 {
+        1
+    }
+
+    #[tracing(complex)]
+    pub async fn async_fn(&self) -> i32 {
+        2
+    }
+
+    fn simple_field(&self) -> i32 {
+        3
+    }
+
+    #[tracing(complex, no_trace)]
+    fn no_trace_complex(&self) -> i32 {
+        4
+    }
+}
+
+#[derive(GraphQLObject)]
+#[graphql(trace = "complex")]
+pub struct DerivedComplex {
+    #[tracing(complex)]
+    complex: bool,
+    #[tracing(complex, fields(test = "magic"))]
+    another_complex: bool,
+
+    /// Simple field
+    sync: i32,
+}
+
+#[graphql_interface(
+    for = [TraceSync],
+    async,
+)]
+trait InterfacedSimple {
+    fn sync_fn(&self) -> i32;
+    async fn async_fn(&self) -> i32;
+}
+
+#[graphql_interface(
+    for = [TraceSync],
+    trace = "trace-sync",
+    async,
+)]
+trait InterfacedSync {
+    fn sync_fn(&self) -> i32;
+    async fn async_fn(&self) -> i32;
+}
+
+#[graphql_interface(
+    for = [TraceAsync],
+    trace = "trace-async",
+    async,
+)]
+trait InterfacedAsync {
+    fn sync_fn(&self) -> i32;
+    async fn async_fn(&self) -> i32;
+}
+
+#[graphql_interface(
+    for = [SkipAll],
+    trace = "skip-all",
+    async,
+)]
+trait InterfacedSkipAll {
+    fn sync_fn(&self) -> i32;
+    async fn async_fn(&self) -> i32;
+}
+
+#[graphql_interface(
+    for = [Complex],
+    trace = "complex",
+    async,
+)]
+trait InterfacedComplex {
+    fn sync_fn(&self) -> i32;
+    #[tracing(complex)]
+    async fn async_fn(&self) -> i32;
+}
+
+macro_rules! build_impl {
+    ($ty:ident, $trt:ident) => {
+        #[graphql_interface(async)]
+        impl $trt for $ty {
+            fn sync_fn(&self) -> i32 {
+                1
+            }
+
+            async fn async_fn(&self) -> i32 {
+                2
+            }
+        }
+    };
+}
+
+build_impl!(TraceSync, InterfacedSimple);
+build_impl!(TraceSync, InterfacedSync);
+build_impl!(TraceAsync, InterfacedAsync);
+build_impl!(SkipAll, InterfacedSkipAll);
+build_impl!(Complex, InterfacedComplex);
diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index d07539238..b144975f0 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -13,5 +13,13 @@ mod subscriptions;
 mod type_info_tests;
 
 #[cfg(test)]
-#[cfg(feature = "traced")]
+#[cfg(feature = "trace")]
 mod trace_tests;
+
+#[cfg(test)]
+#[cfg(feature = "trace-sync")]
+mod trace_sync_tests;
+
+#[cfg(test)]
+#[cfg(feature = "trace-async")]
+mod trace_async_tests;
diff --git a/juniper/src/tests/trace_async_tests.rs b/juniper/src/tests/trace_async_tests.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/juniper/src/tests/trace_async_tests.rs
@@ -0,0 +1 @@
+
diff --git a/juniper/src/tests/trace_sync_tests.rs b/juniper/src/tests/trace_sync_tests.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/juniper/src/tests/trace_sync_tests.rs
@@ -0,0 +1 @@
+
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/trace_tests.rs
index 89d53add8..dfe5dfbde 100644
--- a/juniper/src/tests/trace_tests.rs
+++ b/juniper/src/tests/trace_tests.rs
@@ -1,22 +1,23 @@
-use tracing_test::*;
+use tracing::Level;
 
 use crate::{
     executor::Variables,
     schema::model::RootNode,
-    tests::fixtures::starwars::{model::Database, schema::Query},
+    tests::fixtures::tracing::{
+        schema::{Database, Query},
+        SpanExt as _, TestSubscriber,
+    },
     types::scalars::{EmptyMutation, EmptySubscription},
 };
 
-// TODO: waiting for https://github.com/tokio-rs/tracing/pull/793
-// TODO: async tests
-// TODO: tracing feature needs to be enable when testing
-// cargo test --features tracing
 #[test]
 fn test_execute_sync_clean() {
     let doc = r#"
         {
-            hero {
-                name
+            foo {
+                id
+                nonTraced
+                skipArgument(name: "name?", meaningOfLife: 42)
             }
         }"#;
     let database = Database::new();
@@ -26,31 +27,29 @@ fn test_execute_sync_clean() {
         EmptySubscription::<Database>::new(),
     );
 
-    let span_rule_validation = span("rule_validation");
-    let span_validate_input_values = span("validate_input_values");
-    let span_execute_sync = span("execute_sync");
-
-    let (subscriber, handle) = subscriber::expect()
-        .new_span(span_rule_validation.clone())
-        .enter(span_rule_validation.clone())
-        .exit(span_rule_validation.clone())
-        .drop_span(span_rule_validation.clone())
-        .new_span(span_validate_input_values.clone())
-        .enter(span_validate_input_values.clone())
-        .exit(span_validate_input_values.clone())
-        .drop_span(span_validate_input_values.clone())
-        .new_span(span_execute_sync.clone())
-        .enter(span_execute_sync.clone())
-        .exit(span_execute_sync.clone())
-        .drop_span(span_execute_sync.clone())
-        .done()
-        .run_with_handle();
+    let collector = TestSubscriber::new();
+    let handle = collector.clone();
 
-    tracing::subscriber::with_default(subscriber, || {
+    tracing::subscriber::with_default(collector, || {
         juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).ok();
     });
 
-    handle.assert_finished();
+    handle
+        .assert()
+        .enter_new_span("execute_sync")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.foo")
+        .simple_span("Foo.id")
+        .simple_span(
+            &"Foo.skipArgument"
+                .with_field("meaningOfLife", "42")
+                .with_strict_fields(true),
+        )
+        .close_exited("Query.foo")
+        .close_exited("execute_validated_query")
+        .close_exited("execute_sync");
 }
 
 #[test]
@@ -68,20 +67,441 @@ fn test_execute_sync_with_error() {
         EmptySubscription::<Database>::new(),
     );
 
-    let span_rule_validation = span("rule_validation");
-
-    let (subscriber, handle) = subscriber::expect()
-        .new_span(span_rule_validation.clone())
-        .enter(span_rule_validation.clone())
-        .event(event().with_target("juniper"))
-        .exit(span_rule_validation.clone())
-        .drop_span(span_rule_validation.clone())
-        .done()
-        .run_with_handle();
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
 
     tracing::subscriber::with_default(subscriber, || {
         juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).err();
     });
 
-    handle.assert_finished();
+    handle
+        .assert()
+        .enter_new_span("execute_sync")
+        .enter_new_span("rule_validation")
+        // Test that it writes event to traces when failed to validate rules
+        .event(Level::TRACE, Some("juniper"), vec![])
+        .close_exited("rule_validation")
+        .close_exited("execute_sync");
+}
+
+#[tokio::test]
+async fn test_no_trace_field() {
+    let doc = r#"
+        {
+            foo {
+                nonTraced
+                asyncNonTraced
+            }
+            asyncFoo {
+                nonTraced
+            }
+            fooBars(id: 98) {
+                nonTraced
+                asyncNonTraced
+                ... on Bar {
+                    nonTraced
+                }
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.foo")
+        // In between this two steps should be no other, because `nonTraced` and `asyncNonTraced`
+        // marked with `no_trace`
+        .close_exited("Query.foo")
+        .enter_new_span("Query.asyncFoo")
+        // In between this two steps should be no other, because `nonTraced` marked with `no_trace`
+        .close_exited("Query.asyncFoo")
+        .enter_new_span("Query.fooBars")
+        // Field with name present in interface but resolved on concrete object.
+        .simple_span("Bar.nonTraced")
+        .close_exited("Query.fooBars")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn test_impl_fn_args() {
+    let doc = r#"
+        {
+            foo {
+                skipArgument(name: "don't spy!", meaningOfLife: 42)
+            }
+            bar(id: 37) {
+                defaultArg(another: -1)
+            }
+        }"#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.foo")
+        // Skipped field
+        .simple_span(
+            &"Foo.skipArgument"
+                .with_field("meaningOfLife", "42")
+                .with_strict_fields(true),
+        )
+        .close_exited("Query.foo")
+        // Required argument
+        .new_span(&"Query.bar".with_field("id", "37").with_strict_fields(true))
+        .enter("Query.bar")
+        // Optional, overwritten optional and skipped optional arguments
+        .simple_span(
+            &"Bar.defaultArg"
+                .with_field("this", "42")
+                .with_field("another", "-1")
+                .with_strict_fields(true),
+        )
+        .close_exited("Query.bar")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn test_custom_fields() {
+    let doc = r#"
+        {
+            bar(id: 127) {
+                id
+            }
+            asyncFoo {
+                id
+            }
+        }
+    "#;
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span(&"Query.bar".with_field("id", "127").with_strict_fields(true))
+        // Check whether custom field "self.id" exists
+        .simple_span(
+            &"Bar.id"
+                .with_field("self.id", "127")
+                .with_strict_fields(true),
+        )
+        .close_exited("Query.bar")
+        .enter_new_span("Query.asyncFoo")
+        // Check multiple custom arguments and const arguments
+        .simple_span(
+            &"DerivedFoo.id"
+                .with_field("self.id", "42")
+                .with_field("custom_fields", "\"work\"")
+                .with_strict_fields(true),
+        )
+        .close_exited("Query.asyncFoo")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn overwrite_level_and_target() {
+    let doc = r#"
+        {
+            foo {
+                target
+                level
+            }
+            asyncFoo {
+                target
+                level
+            }
+            fooBar {
+                target
+                level
+            }
+        }
+        "#;
+
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.foo")
+        .simple_span(
+            &"Foo.target"
+                // Check on overwritten target in #[graphql_object]
+                .with_target("my_target")
+                // Check on default level in #[graphql_object]
+                .with_level(Level::INFO),
+        )
+        .simple_span(
+            &"Foo.level"
+                // Check on default target in #[graphql_object]
+                .with_target("juniper::tests::fixtures::tracing::schema")
+                // Check on overwritten level in #[graphql_object]
+                .with_level(Level::WARN),
+        )
+        .close_exited("Query.foo")
+        .enter_new_span("Query.asyncFoo")
+        .simple_span(
+            &"DerivedFoo.target"
+                // Check on overwritten target in derived GraphQLObject
+                .with_target("my_target")
+                // Check on default level in derived GraphQLObject
+                .with_level(Level::INFO),
+        )
+        .simple_span(
+            &"DerivedFoo.level"
+                // Check on default target in derived GraphQLObject
+                .with_target("juniper::tests::fixtures::tracing::schema")
+                // Check on overwritten level in derived GraphQLObject
+                .with_level(Level::WARN),
+        )
+        .close_exited("Query.asyncFoo")
+        .enter_new_span("Query.fooBar")
+        .simple_span(
+            &"FooBar.target"
+                // Check on overwritten target in #[graphql_interface]
+                .with_target("my_target")
+                // Check on default level in #[graphql_interface]
+                .with_level(Level::INFO),
+        )
+        .simple_span(
+            &"FooBar.level"
+                // Check on default target in #[graphql_interface]
+                .with_target("juniper::tests::fixtures::tracing::schema")
+                // Check on overwritten level in #[graphql_interface]
+                .with_level(Level::WARN),
+        )
+        .close_exited("Query.fooBar")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn graphql_object_trace_arg() {
+    let doc = r#"
+        {
+            traceAsync {
+                asyncFn
+                syncFn
+            }
+            derivedAsync {
+                sync
+            }
+            traceSync {
+                asyncFn
+                syncFn
+            }
+            derivedSync {
+                sync
+            }
+            skipAll {
+                asyncFn
+                syncFn
+            }
+            skipAllDerived {
+                sync
+            }
+            complexSync {
+                syncFn
+                asyncFn
+                simpleField
+                noTraceComplex
+            }
+            complexDerived {
+                complex
+                anotherComplex
+                sync
+            }
+        }
+        "#;
+
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.traceAsync")
+        .simple_span("TraceAsync.asyncFn")
+        // There shouldn't be span for `syncFn` because it's not async
+        .close_exited("Query.traceAsync")
+        // There should be nothing because derived resolvers sync by nature
+        .simple_span("Query.derivedAsync")
+        // There shouldn't be span for `asyncFn` because it's async
+        .enter_new_span("Query.traceSync")
+        .simple_span("TraceSync.syncFn")
+        .close_exited("Query.traceSync")
+        .enter_new_span("Query.derivedSync")
+        .simple_span("SyncDerived.sync")
+        .close_exited("Query.derivedSync")
+        // There shouldn't be any spans because `SkipAll` and `SkipAllDerived` marked with "skip-all"
+        .simple_span("Query.skipAll")
+        .simple_span("Query.skipAllDerived")
+        .enter_new_span("Query.complexSync")
+        .simple_span("Complex.syncFn")
+        .simple_span("Complex.asyncFn")
+        // There shouldn't be any spans for `simpleField` and `noTraceComplex`
+        .close_exited("Query.complexSync")
+        .enter_new_span("Query.complexDerived")
+        .simple_span("DerivedComplex.complex")
+        .simple_span(
+            &"DerivedComplex.anotherComplex"
+                .with_field("test", "\"magic\"")
+                .with_strict_fields(true),
+        )
+        // There shouldn't be span for `sync` because it's not marked with `#[tracing(complex)]`
+        .close_exited("Query.complexDerived")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn graphql_interface_trace_arg() {
+    let doc = r#"
+        {
+            erasedSimple {
+                syncFn
+                asyncFn
+            }
+            erasedSync {
+                syncFn
+                asyncFn
+            }
+            erasedAsync {
+                syncFn
+                asyncFn
+            }
+            erasedSkipAll {
+                syncFn
+                asyncFn
+            }
+            erasedComplex {
+                syncFn
+                asyncFn
+            }
+        }
+        "#;
+
+    let database = Database::new();
+    let schema = RootNode::new(
+        Query,
+        EmptyMutation::<Database>::new(),
+        EmptySubscription::<Database>::new(),
+    );
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    juniper::execute(doc, None, &schema, &Variables::new(), &database)
+        .await
+        .err();
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.erasedSimple")
+        .simple_span("InterfacedSimple.syncFn")
+        .simple_span("InterfacedSimple.asyncFn")
+        .close_exited("Query.erasedSimple")
+        .enter_new_span("Query.erasedSync")
+        .simple_span("InterfacedSync.syncFn")
+        .close_exited("Query.erasedSync")
+        .enter_new_span("Query.erasedAsync")
+        .simple_span("InterfacedAsync.asyncFn")
+        .close_exited("Query.erasedAsync")
+        .simple_span("Query.erasedSkipAll")
+        .enter_new_span("Query.erasedComplex")
+        .simple_span("InterfacedComplex.asyncFn")
+        .close_exited("Query.erasedComplex")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
 }
diff --git a/juniper/src/util.rs b/juniper/src/util.rs
index 08f51493d..596297bfe 100644
--- a/juniper/src/util.rs
+++ b/juniper/src/util.rs
@@ -51,3 +51,22 @@ fn test_to_camel_case() {
     assert_eq!(&to_camel_case("a")[..], "a");
     assert_eq!(&to_camel_case("")[..], "");
 }
+
+#[cfg(feature = "tracing-futures")]
+#[doc(hidden)]
+pub mod tracing {
+    use tracing::Span;
+    use tracing_futures::{Instrument, Instrumented};
+
+    /// Required to pass sanity tests when `Instrument` already imported.
+    pub trait InstrumentInternal {
+        fn __instrument(self, span: Span) -> Instrumented<Self>
+        where
+            Self: Sized,
+        {
+            self.instrument(span)
+        }
+    }
+
+    impl<T> InstrumentInternal for T where T: Instrument {}
+}
diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml
index 54061eb98..47642bd30 100644
--- a/juniper_codegen/Cargo.toml
+++ b/juniper_codegen/Cargo.toml
@@ -18,7 +18,8 @@ travis-ci = { repository = "graphql-rust/juniper" }
 proc-macro = true
 
 [features]
-traced = []
+trace-async = []
+trace-sync = []
 
 [dependencies]
 proc-macro-error = "1.0.2"
diff --git a/juniper_codegen/src/common/gen.rs b/juniper_codegen/src/common/gen.rs
index dc5adc139..2a549536a 100644
--- a/juniper_codegen/src/common/gen.rs
+++ b/juniper_codegen/src/common/gen.rs
@@ -30,7 +30,10 @@ pub(crate) fn sync_resolving_code() -> TokenStream {
 /// [`Future`]: std::future::Future
 /// [`Future::Output`]: std::future::Future::Output
 /// [1]: https://spec.graphql.org/June2018/#sec-Types
-pub(crate) fn async_resolving_code(ty: Option<&syn::Type>) -> TokenStream {
+pub(crate) fn async_resolving_code(
+    ty: Option<&syn::Type>,
+    trace_async: TokenStream,
+) -> TokenStream {
     let ty = ty.map(|t| quote! { : #t });
 
     quote! {
@@ -42,6 +45,6 @@ pub(crate) fn async_resolving_code(ty: Option<&syn::Type>) -> TokenStream {
                 },
                 None => Ok(::juniper::Value::null()),
             }
-        }))
+        }) #trace_async)
     }
 }
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index a1298ba69..7abf394e6 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -148,6 +148,8 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
         include_type_generics: true,
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
+
+        tracing_rule: attrs.tracing_rule,
     };
 
     Ok(definition.into_enum_tokens())
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index 78f36124e..c10b580fb 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -146,6 +146,8 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
         include_type_generics: true,
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
+
+        tracing_rule: attrs.tracing_rule,
     };
 
     Ok(definition.into_input_object_tokens())
diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs
index 356867226..fed1ff57b 100644
--- a/juniper_codegen/src/derive_object.rs
+++ b/juniper_codegen/src/derive_object.rs
@@ -132,6 +132,8 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
         include_type_generics: true,
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
+
+        tracing_rule: attrs.tracing_rule,
     };
 
     Ok(definition.into_tokens())
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 44187befa..66f40bdc8 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -12,7 +12,7 @@ use crate::{
         ScalarValueType,
     },
     result::GraphQLScope,
-    util::{path_eq_single, span_container::SpanContainer, to_camel_case},
+    util::{path_eq_single, span_container::SpanContainer, to_camel_case, tracing},
 };
 
 use super::{
@@ -210,6 +210,8 @@ pub fn expand_on_trait(
 
         fields,
         implementers,
+
+        tracing_rule: meta.tracing.map(|t| *t.inner()),
     };
 
     // Attach the `juniper::AsDynGraphQLValue` on top of the trait if dynamic dispatch is used.
@@ -344,7 +346,7 @@ pub fn expand_on_impl(
 }
 
 /// Representation of parsed Rust trait method for `#[graphql_interface]` macro code generation.
-enum TraitMethod {
+pub(super) enum TraitMethod {
     /// Method represents a [`Field`] of [GraphQL interface][1].
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
@@ -501,6 +503,8 @@ impl TraitMethod {
             method: method_ident.clone(),
             arguments,
             is_async: method.sig.asyncness.is_some(),
+
+            tracing: tracing::Attr::from_method(method),
         })
     }
 
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index 78dfa81af..33ff96b43 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -3,10 +3,15 @@
 //! [1]: https://spec.graphql.org/June2018/#sec-Interfaces
 
 pub mod attr;
+mod tracing;
 
-use std::collections::{HashMap, HashSet};
+use std::{
+    collections::{HashMap, HashSet},
+    str::FromStr,
+};
 
 use proc_macro2::TokenStream;
+use proc_macro_error::abort;
 use quote::{format_ident, quote, ToTokens, TokenStreamExt as _};
 use syn::{
     parse::{Parse, ParseStream},
@@ -119,6 +124,8 @@ struct TraitMeta {
     /// Indicator whether the generated code is intended to be used only inside the [`juniper`]
     /// library.
     is_internal: bool,
+
+    tracing: Option<SpanContainer<tracing::TracingRule>>,
 }
 
 impl Parse for TraitMeta {
@@ -217,6 +224,26 @@ impl Parse for TraitMeta {
                 "internal" => {
                     output.is_internal = true;
                 }
+                "trace" => {
+                    let span = ident.span();
+                    input.parse::<token::Eq>()?;
+                    let tracing = input.parse::<syn::LitStr>()?;
+                    let tracing_rule = tracing::TracingRule::from_str(tracing.value().as_str());
+                    match tracing_rule {
+                        Ok(rule) => output
+                            .tracing
+                            .replace(SpanContainer::new(span, Some(tracing.span()), rule))
+                            .none_or_else(|_| err::dup_arg(span))?,
+                        Err(_) => abort!(syn::Error::new(
+                            tracing.span(),
+                            format!(
+                                "Unknown tracing rule: {}, \
+                             known values: trace-sync, trace-async, skip-all and complex",
+                                tracing.value(),
+                            )
+                        )),
+                    }
+                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -244,6 +271,7 @@ impl TraitMeta {
                 external_downcasts: self, another => span_joined
             ),
             is_internal: self.is_internal || another.is_internal,
+            tracing: try_merge_opt!(tracing: self, another),
         })
     }
 
@@ -751,6 +779,8 @@ struct Definition {
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     implementers: Vec<Implementer>,
+
+    tracing_rule: Option<tracing::TracingRule>,
 }
 
 impl Definition {
@@ -845,7 +875,7 @@ impl Definition {
         let fields_resolvers = self
             .fields
             .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(&trait_ty));
+            .filter_map(|f| f.method_resolve_field_tokens(&trait_ty, self));
         let async_fields_panic = {
             let names = self
                 .fields
@@ -959,7 +989,7 @@ impl Definition {
         let fields_resolvers = self
             .fields
             .iter()
-            .map(|f| f.method_resolve_field_async_tokens(&trait_ty));
+            .map(|f| f.method_resolve_field_async_tokens(&trait_ty, &self));
         let no_field_panic = self.panic_no_field_tokens();
 
         let custom_downcasts = self
@@ -968,6 +998,8 @@ impl Definition {
             .filter_map(|i| i.method_resolve_into_type_async_tokens(&trait_ty));
         let regular_downcast = self.ty.method_resolve_into_type_async_tokens();
 
+        let instrument = tracing::instrument();
+
         quote! {
             #[automatically_derived]
             impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty #where_clause
@@ -979,6 +1011,8 @@ impl Definition {
                     args: &'b ::juniper::Arguments<#scalar>,
                     executor: &'b ::juniper::Executor<Self::Context, #scalar>,
                 ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
+                    #instrument
+
                     match field {
                         #( #fields_resolvers )*
                         _ => #no_field_panic,
@@ -1272,6 +1306,8 @@ struct Field {
     ///
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     is_async: bool,
+
+    tracing: Option<tracing::Attr>,
 }
 
 impl Field {
@@ -1317,7 +1353,11 @@ impl Field {
     ///
     /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
     #[must_use]
-    fn method_resolve_field_tokens(&self, trait_ty: &syn::Type) -> Option<TokenStream> {
+    fn method_resolve_field_tokens(
+        &self,
+        trait_ty: &syn::Type,
+        definition: &Definition,
+    ) -> Option<TokenStream> {
         if self.is_async {
             return None;
         }
@@ -1331,8 +1371,19 @@ impl Field {
 
         let resolving_code = gen::sync_resolving_code();
 
+        let (tracing_span, trace_sync) = if cfg!(feature = "trace-sync") {
+            (
+                tracing::span_tokens(definition, self),
+                tracing::sync_tokens(definition, self),
+            )
+        } else {
+            (quote!(), quote!())
+        };
+
         Some(quote! {
             #name => {
+                #tracing_span
+                #trace_sync
                 let res: #ty = <Self as #trait_ty>::#method(self #( , #arguments )*);
                 #resolving_code
             }
@@ -1344,7 +1395,11 @@ impl Field {
     ///
     /// [`GraphQLValueAsync::resolve_field_async`]: juniper::GraphQLValueAsync::resolve_field_async
     #[must_use]
-    fn method_resolve_field_async_tokens(&self, trait_ty: &syn::Type) -> TokenStream {
+    fn method_resolve_field_async_tokens(
+        &self,
+        trait_ty: &syn::Type,
+        definition: &Definition,
+    ) -> TokenStream {
         let (name, ty, method) = (&self.name, &self.ty, &self.method);
 
         let arguments = self
@@ -1357,10 +1412,20 @@ impl Field {
             fut = quote! { ::juniper::futures::future::ready(#fut) };
         }
 
-        let resolving_code = gen::async_resolving_code(Some(ty));
+        let (tracing_span, trace_async) = if cfg!(feature = "trace-async") {
+            (
+                tracing::span_tokens(definition, self),
+                tracing::async_tokens(definition, self),
+            )
+        } else {
+            (quote!(), quote!())
+        };
+
+        let resolving_code = gen::async_resolving_code(Some(ty), trace_async);
 
         quote! {
             #name => {
+                #tracing_span
                 let fut = #fut;
                 #resolving_code
             }
@@ -1522,8 +1587,7 @@ impl Implementer {
         let scalar = &self.scalar;
 
         let downcast = self.downcast_call_tokens(trait_ty, None);
-
-        let resolving_code = gen::async_resolving_code(None);
+        let resolving_code = gen::async_resolving_code(None, quote!());
 
         Some(quote! {
             if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
@@ -1959,7 +2023,7 @@ impl EnumType {
     /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
     #[must_use]
     fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
-        let resolving_code = gen::async_resolving_code(None);
+        let resolving_code = gen::async_resolving_code(None, quote!());
 
         let match_arms = self.variants.iter().map(|ty| {
             let variant = Self::variant_ident(ty);
@@ -2141,7 +2205,7 @@ impl TraitObjectType {
     /// [0]: juniper::GraphQLValueAsync::resolve_into_type_async
     #[must_use]
     fn method_resolve_into_type_async_tokens(&self) -> TokenStream {
-        let resolving_code = gen::async_resolving_code(None);
+        let resolving_code = gen::async_resolving_code(None, quote!());
 
         quote! {
             let fut = ::juniper::futures::future::ready(self.as_dyn_graphql_value_async());
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
new file mode 100644
index 000000000..4f9403f8d
--- /dev/null
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -0,0 +1,53 @@
+pub use crate::util::tracing::{
+    async_tokens, instrument, span_tokens, sync_tokens, Attr, TracedArgument, TracedField,
+    TracedType, TracingRule,
+};
+
+use super::{Definition, Field, FieldArgument};
+
+impl TracedType for Definition {
+    fn tracing_rule(&self) -> Option<TracingRule> {
+        self.tracing_rule
+    }
+
+    fn name(&self) -> &str {
+        self.name.as_str()
+    }
+
+    fn scalar(&self) -> Option<syn::Type> {
+        Some(self.scalar.ty())
+    }
+}
+
+impl TracedField for Field {
+    type Arg = FieldArgument;
+
+    fn tracing_attr(&self) -> Option<&Attr> {
+        self.tracing.as_ref()
+    }
+
+    fn is_async(&self) -> bool {
+        self.is_async
+    }
+
+    fn name(&self) -> &str {
+        self.name.as_str()
+    }
+
+    fn args(&self) -> Vec<&Self::Arg> {
+        self.arguments
+            .iter()
+            .filter_map(|arg| arg.as_regular())
+            .collect()
+    }
+}
+
+impl TracedArgument for FieldArgument {
+    fn ty(&self) -> &syn::Type {
+        &self.ty
+    }
+
+    fn name(&self) -> &str {
+        self.name.as_str()
+    }
+}
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 30ef327d5..916e4f534 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -226,6 +226,8 @@ fn create(
         include_type_generics: false,
         generic_scalar: true,
         no_async: _impl.attrs.no_async.is_some(),
+
+        tracing_rule: _impl.attrs.tracing_rule,
     };
 
     Ok(definition)
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index 38870ff28..7e1196709 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -144,7 +144,7 @@ pub fn derive_input_object(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLObject, attributes(graphql))]
+#[proc_macro_derive(GraphQLObject, attributes(graphql, tracing))]
 pub fn derive_object(input: TokenStream) -> TokenStream {
     let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
     let gen = derive_object::build_derive_object(ast, GraphQLScope::DeriveObject);
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 3667ca65f..a5cd850bc 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -3,6 +3,7 @@
 pub mod duplicate;
 pub mod parse_impl;
 pub mod span_container;
+pub mod tracing;
 
 use std::{collections::HashMap, str::FromStr};
 
@@ -18,6 +19,7 @@ use syn::{
     spanned::Spanned,
     token, Attribute, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
 };
+use tracing::{Attr, TracingRule};
 
 use crate::common::parse::ParseBufferExt as _;
 
@@ -141,51 +143,6 @@ fn get_deprecated_meta_list(list: &MetaList) -> DeprecationAttr {
     DeprecationAttr { reason: None }
 }
 
-#[derive(Debug, Default)]
-pub struct TracingAttr {
-    name: Option<SpanContainer<syn::LitStr>>,
-    skip: HashMap<String, SpanContainer<syn::Ident>>,
-    no_trace: bool,
-}
-
-impl Parse for TracingAttr {
-    fn parse(input: ParseStream) -> syn::Result<Self> {
-        let mut attr = TracingAttr::default();
-
-        let content;
-        syn::parenthesized!(content in input);
-        while !content.is_empty() {
-            let name = content.parse::<syn::Ident>()?;
-
-            match name.to_string().as_str() {
-                "name" => {
-                    content.parse::<token::Eq>()?;
-                    let val: syn::LitStr = content.parse()?;
-                    attr.name = Some(SpanContainer::new(name.span(), Some(val.span()), val));
-                }
-                "skip" => {
-                    let skipped_fields;
-                    syn::parenthesized!(skipped_fields in content);
-                    while !skipped_fields.is_empty() {
-                        let field: syn::Ident = skipped_fields.parse()?;
-                        attr.skip.insert(field.to_string(), SpanContainer::new(field.span(), None, field));
-
-                        skipped_fields.parse::<token::Comma>().ok();
-                    }
-                }
-                "no_trace" => {
-                    attr.no_trace = true;
-                }
-                _ => return Err(syn::Error::new(name.span(), "unknown attribute")),
-            }
-
-            // Discard trailing comma.
-            content.parse::<token::Comma>().ok();
-        }
-        Ok(attr)
-    }
-}
-
 // Gets doc comment.
 pub fn get_doc_comment(attrs: &[Attribute]) -> Option<SpanContainer<String>> {
     if let Some(items) = get_doc_attr(attrs) {
@@ -383,6 +340,8 @@ pub struct ObjectAttributes {
     pub no_async: Option<SpanContainer<()>>,
     pub is_internal: bool,
     pub rename: Option<RenameRule>,
+
+    pub tracing_rule: Option<TracingRule>,
 }
 
 impl Parse for ObjectAttributes {
@@ -453,6 +412,15 @@ impl Parse for ObjectAttributes {
                         return Err(syn::Error::new(val.span(), "unknown rename rule"));
                     }
                 }
+                "trace" => {
+                    input.parse::<syn::token::Eq>()?;
+                    let val = input.parse::<syn::LitStr>()?;
+                    if let Ok(trace) = TracingRule::from_str(&val.value()) {
+                        output.tracing_rule = Some(trace);
+                    } else {
+                        return Err(syn::Error::new(val.span(), "unknown tracing skip rule"));
+                    }
+                }
                 _ => {
                     return Err(syn::Error::new(ident.span(), "unknown attribute"));
                 }
@@ -636,7 +604,7 @@ pub struct FieldAttributes {
     /// Only relevant for object input objects.
     pub default: Option<SpanContainer<Option<syn::Expr>>>,
     // Only relevant when `tracing` feature enabled
-    pub tracing: Option<TracingAttr>
+    pub tracing: Option<Attr>,
 }
 
 impl Parse for FieldAttributes {
@@ -699,7 +667,7 @@ impl FieldAttributes {
             output.deprecation = deprecation;
         }
 
-        let tracing_attr = attrs.iter().find(|attr| attr.path.is_ident("traced"));
+        let tracing_attr = attrs.iter().find(|attr| attr.path.is_ident("tracing"));
 
         if let Some(attr) = tracing_attr {
             output.tracing = Some(attr.parse_args()?);
@@ -727,7 +695,7 @@ pub struct GraphQLTypeDefinitionField {
     pub _type: syn::Type,
     pub description: Option<String>,
     pub deprecation: Option<DeprecationAttr>,
-    pub tracing: Option<TracingAttr>,
+    pub tracing: Option<Attr>,
     pub args: Vec<GraphQLTypeDefinitionFieldArg>,
     pub resolver_code: TokenStream,
     pub is_type_inferred: bool,
@@ -775,6 +743,8 @@ pub struct GraphQLTypeDefiniton {
     pub generic_scalar: bool,
     // FIXME: make this redundant.
     pub no_async: bool,
+
+    pub tracing_rule: Option<TracingRule>,
 }
 
 impl GraphQLTypeDefiniton {
@@ -875,6 +845,15 @@ impl GraphQLTypeDefiniton {
                     },
                 )
             } else {
+                let (tracing_span, trace_sync) = if cfg!(feature = "trace-sync") {
+                    (
+                        tracing::span_tokens(&self, field),
+                        tracing::sync_tokens(&self, field),
+                    )
+                } else {
+                    (quote!(), quote!())
+                };
+
                 let _type = if field.is_type_inferred {
                     quote!()
                 } else {
@@ -883,6 +862,8 @@ impl GraphQLTypeDefiniton {
                 };
                 quote!(
                     #name => {
+                        #tracing_span
+                        #trace_sync
                         let res #_type = (|| { #code })();
                         ::juniper::IntoResolvable::into(
                             res,
@@ -940,6 +921,8 @@ impl GraphQLTypeDefiniton {
         };
         let (impl_generics, _, where_clause) = generics.split_for_impl();
 
+        let tracing_instrument = tracing::instrument();
+
         let resolve_field_async = {
             let resolve_matches_async = self.fields.iter().map(|field| {
                 let name = &field.name;
@@ -951,46 +934,17 @@ impl GraphQLTypeDefiniton {
                     quote!(: #_type)
                 };
 
-                let tracing = if cfg!(feature = "traced") {
-                    let span_name = format!("{}.{}", self.name, name);
-                    let span_name = syn::LitStr::new(&span_name, name.span());
-
-                    let args = field.args.iter().filter_map(|arg| {
-                        let name = &arg.name;
-                        let arg_name = syn::Ident::new(name, arg._type.span());
-                        let arg_getter = syn::LitStr::new(name, arg._type.span());
-                        let scalar = &self.scalar.clone().unwrap_or_else(
-                            || syn::parse2(quote!(::juniper::DefaultScalarValue)).unwrap(),
-                        );
-                        let ty = &arg._type;
-                        field.tracing
-                            .as_ref()
-                            .map(|t| t.skip.get(name))
-                            .flatten()
-                            .is_none()
-                            .then(|| {
-                                quote!(
-                                    #arg_name = ::juniper::tracing::field::debug(
-                                        args.get::<#ty>(#arg_getter).unwrap_or_else(|| {
-                                            ::juniper::FromInputValue::<#scalar>::from_implicit_null()
-                                        })
-                                    )
-                                )
-                            })
-                    });
-
-                    let wrapper = quote!(
-                        .instrument(::juniper::tracing::trace_span!(#span_name, #(#args, )*))
-                    );
-
-                    Some(wrapper)
-                } else {
-                    None
-                };
-
+                let tracing_span = tracing::span_tokens(&self, field);
                 if field.is_async {
+                    let trace_async = if cfg!(feature = "trace-async") {
+                        tracing::async_tokens(&self, field)
+                    } else {
+                        quote!()
+                    };
+
                     quote!(
                         #name => {
+                            #tracing_span
                             let f = async move {
                                 let res #_type = async move { #code }.await;
 
@@ -1008,11 +962,20 @@ impl GraphQLTypeDefiniton {
                                     Ok(None) => Ok(::juniper::Value::null()),
                                     Err(e) => Err(e),
                                 }
-                            } #tracing;
+                            } #trace_async;
                             Box::pin(f)
                         },
                     )
                 } else {
+                    let (trace_async, trace_sync) = if cfg!(feature = "trace-sync") {
+                        (
+                            tracing::async_tokens(&self, field),
+                            tracing::sync_tokens(&self, field),
+                        )
+                    } else {
+                        (quote!(), quote!())
+                    };
+
                     let inner = if !self.no_async {
                         quote!(
                             let f = async move {
@@ -1024,12 +987,13 @@ impl GraphQLTypeDefiniton {
                                     Ok(None) => Ok(::juniper::Value::null()),
                                     Err(e) => Err(e),
                                 }
-                            };
+                            } #trace_async;
                             use ::juniper::futures::future;
                             future::FutureExt::boxed(f)
                         )
                     } else {
                         quote!(
+                            #trace_sync
                             let v = match res2 {
                                 Ok(Some((ctx, r))) => executor.replaced_context(ctx).resolve_with_ctx(&(), &r),
                                 Ok(None) => Ok(::juniper::Value::null()),
@@ -1042,11 +1006,15 @@ impl GraphQLTypeDefiniton {
 
                     quote!(
                         #name => {
-                            let res #_type = (||{ #code })();
-                            let res2 = ::juniper::IntoResolvable::into(
-                                res,
-                                executor.context()
-                            );
+                            #tracing_span
+                            let res2 = {
+                                #trace_sync
+                                let res #_type = (||{ #code })();
+                                ::juniper::IntoResolvable::into(
+                                    res,
+                                    executor.context()
+                                )
+                            };
                             #inner
                         },
                     )
@@ -1084,12 +1052,6 @@ impl GraphQLTypeDefiniton {
                 None
             };
 
-            let instrument = if cfg!(feature = "traced") {
-                Some(quote!(use ::juniper::tracing::Instrument as _;))
-            } else {
-                None
-            };
-
             quote!(
                 impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty #type_generics_tokens
                     #where_async
@@ -1105,7 +1067,7 @@ impl GraphQLTypeDefiniton {
                     {
                         use ::juniper::futures::future;
                         use ::juniper::GraphQLType;
-                        #instrument;
+                        #tracing_instrument;
                         match field {
                             #( #resolve_matches_async )*
                             _ => {
@@ -1193,6 +1155,8 @@ impl GraphQLTypeDefiniton {
                     args: &::juniper::Arguments<#scalar>,
                     executor: &::juniper::Executor<Self::Context, #scalar>,
                 ) -> ::juniper::ExecutionResult<#scalar> {
+                    #tracing_instrument
+
                     match field {
                         #( #resolve_matches )*
                         _ => {
@@ -1366,8 +1330,18 @@ impl GraphQLTypeDefiniton {
                     let _type_name = &field._type;
                     _type = quote!(: #_type_name);
                 };
+
+                let (trace_span, trace_async) = if cfg!(feature = "trace-async") {
+                    (
+                        tracing::span_tokens(&self, field),
+                        tracing::async_tokens(&self, field),
+                    )
+                } else {
+                    (quote!(), quote!())
+                };
                 quote!(
                     #name => {
+                        #trace_span
                         ::juniper::futures::FutureExt::boxed(async move {
                             let res #_type = async { #code }.await;
                             let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
@@ -1389,7 +1363,7 @@ impl GraphQLTypeDefiniton {
                                         Err(e) => Err(ex.new_error(e)),
                                     }
                                 }
-                            });
+                            }) #trace_async;
                             Ok(
                                 ::juniper::Value::Scalar::<
                                     ::juniper::ValuesStream::<#scalar>
@@ -1482,6 +1456,8 @@ impl GraphQLTypeDefiniton {
             }
         );
 
+        let instrument = tracing::instrument();
+
         let subscription_implementation = quote!(
             impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens
             #where_clause_with_send_sync
@@ -1514,6 +1490,7 @@ impl GraphQLTypeDefiniton {
                 {
                     use ::juniper::Value;
                     use ::juniper::futures::stream::StreamExt as _;
+                    #instrument
 
                     match field_name {
                             #( #resolve_matches_async )*
diff --git a/juniper_codegen/src/util/tracing.rs b/juniper_codegen/src/util/tracing.rs
new file mode 100644
index 000000000..bf420c817
--- /dev/null
+++ b/juniper_codegen/src/util/tracing.rs
@@ -0,0 +1,338 @@
+use std::{collections::HashMap, mem, str::FromStr};
+
+use proc_macro2::TokenStream;
+use proc_macro_error::abort;
+use quote::quote;
+use syn::{
+    parse::{Parse, ParseStream},
+    spanned::Spanned as _,
+    token,
+};
+
+#[derive(Debug, Default)]
+pub struct Attr {
+    name: Option<syn::LitStr>,
+    level: Option<syn::LitStr>,
+    target: Option<syn::LitStr>,
+    skip: HashMap<String, syn::Ident>,
+    fields: Vec<syn::ExprAssign>,
+    is_complex: bool,
+    no_trace: bool,
+}
+
+impl Attr {
+    /// Parses [`TracingAttr`] from `method`s attributes and removes itself from
+    /// `method.attrs` if present.
+    pub fn from_method(method: &mut syn::TraitItemMethod) -> Option<Self> {
+        let attr = method
+            .attrs
+            .iter()
+            .find(|attr| attr.path.is_ident("tracing"))
+            .map(|attr| attr.parse_args())
+            .transpose();
+
+        method.attrs = mem::take(&mut method.attrs)
+            .into_iter()
+            .filter(|attr| !attr.path.is_ident("tracing"))
+            .collect();
+
+        match attr {
+            Ok(attr) => attr,
+            Err(e) => abort!(e),
+        }
+    }
+}
+
+impl Parse for Attr {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let mut attr = Attr::default();
+
+        while !input.is_empty() {
+            let name = input.parse::<syn::Ident>()?;
+
+            match name.to_string().as_str() {
+                "name" => {
+                    input.parse::<token::Eq>()?;
+                    attr.name = Some(input.parse()?);
+                }
+                "level" => {
+                    input.parse::<token::Eq>()?;
+                    attr.level = Some(input.parse()?);
+                }
+                "target" => {
+                    input.parse::<token::Eq>()?;
+                    attr.target = Some(input.parse()?);
+                }
+                "skip" => {
+                    let skipped_fields;
+                    syn::parenthesized!(skipped_fields in input);
+                    while !skipped_fields.is_empty() {
+                        let field: syn::Ident = skipped_fields.parse()?;
+                        attr.skip.insert(field.to_string(), field);
+
+                        skipped_fields.parse::<token::Comma>().ok();
+                    }
+                }
+                "no_trace" => {
+                    attr.no_trace = true;
+                }
+                "complex" => {
+                    attr.is_complex = true;
+                }
+                "fields" => {
+                    let fields;
+                    syn::parenthesized!(fields in input);
+                    while !fields.is_empty() {
+                        attr.fields.push(fields.parse()?);
+
+                        fields.parse::<token::Comma>().ok();
+                    }
+                }
+                _ => return Err(syn::Error::new(name.span(), "unknown attribute")),
+            }
+
+            // Discard trailing comma.
+            input.parse::<token::Comma>().ok();
+        }
+        Ok(attr)
+    }
+}
+
+/// The different possible groups of fields to trace.
+#[derive(Copy, Clone, Debug)]
+pub enum TracingRule {
+    /// Trace all fields that resolved using `async fn`s.
+    Async,
+
+    /// Trace all fields that can be syncronously resolved.
+    Sync,
+
+    /// Trace only fields that marked with `#[tracing(complex)]`.
+    Complex,
+
+    /// Skip tracing of all fields.
+    SkipAll,
+}
+
+impl TracingRule {
+    #[cfg(any(feature = "trace-async", feature = "trace-sync"))]
+    pub fn is_traced(&self, field: &impl TracedField) -> bool {
+        match self {
+            Self::Async => field.is_async(),
+            Self::Sync => !field.is_async(),
+            Self::Complex => field.tracing_attr().map_or(false, |t| t.is_complex),
+            Self::SkipAll => false,
+        }
+    }
+}
+
+impl FromStr for TracingRule {
+    type Err = ();
+
+    fn from_str(rule: &str) -> Result<Self, Self::Err> {
+        match rule {
+            "trace-async" => Ok(Self::Async),
+            "trace-sync" => Ok(Self::Sync),
+            "skip-all" => Ok(Self::SkipAll),
+            "complex" => Ok(Self::Complex),
+            _ => Err(()),
+        }
+    }
+}
+
+pub trait TracedType {
+    fn tracing_rule(&self) -> Option<TracingRule>;
+    fn name(&self) -> &str;
+    fn scalar(&self) -> Option<syn::Type>;
+}
+
+pub trait TracedField {
+    type Arg: TracedArgument;
+
+    fn tracing_attr(&self) -> Option<&Attr>;
+    fn is_async(&self) -> bool;
+    fn name(&self) -> &str;
+    fn args(&self) -> Vec<&Self::Arg>;
+}
+
+pub trait TracedArgument {
+    fn ty(&self) -> &syn::Type;
+    fn name(&self) -> &str;
+}
+
+#[cfg(any(feature = "trace-async", feature = "trace-sync"))]
+fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
+    let traced = ty
+        .tracing_rule()
+        .map_or_else(|| true, |rule| rule.is_traced(field));
+
+    let no_trace = field.tracing_attr().map(|t| t.no_trace).unwrap_or(false);
+
+    traced && !no_trace
+}
+
+#[cfg(not(any(feature = "trace-async", feature = "trace-sync")))]
+fn is_traced(_: &impl TracedType, _: &impl TracedField) -> bool {
+    false
+}
+
+#[cfg(any(feature = "trace-async", feature = "trace-sync"))]
+pub fn instrument() -> TokenStream {
+    quote!(
+        use ::juniper::InstrumentInternal as _;
+    )
+}
+
+#[cfg(not(any(feature = "trace-async", feature = "trace-sync")))]
+pub fn instrument() -> TokenStream {
+    quote!()
+}
+
+// Returns code that constructs `span` required for tracing
+pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field) {
+        return quote!();
+    }
+
+    let name = field.name();
+    let span_name = format!("{}.{}", ty.name(), name);
+    let span_name = syn::LitStr::new(&span_name, name.span());
+
+    let args = field.args().into_iter().filter_map(|arg| {
+        let name = arg.name();
+        let arg_name = syn::LitStr::new(name, arg.ty().span());
+        let arg_getter = syn::LitStr::new(name, arg.ty().span());
+        let scalar = &ty
+            .scalar()
+            .unwrap_or_else(|| syn::parse2(quote!(::juniper::DefaultScalarValue)).unwrap());
+        let ty = arg.ty();
+
+        field
+            .tracing_attr()
+            .map(|t| t.skip.get(name))
+            .flatten()
+            .is_none()
+            .then(|| {
+                quote!(
+                    #arg_name = ::juniper::tracing::field::debug(
+                        args.get::<#ty>(#arg_getter).unwrap_or_else(|| {
+                            ::juniper::FromInputValue::<#scalar>::from_implicit_null()
+                        })
+                    )
+                )
+            })
+    });
+
+    let args: Vec<_> = if let Some(tracing) = field.tracing_attr() {
+        let additional_fields = tracing.fields.iter().map(|f| {
+            let name = &f.left;
+            let right = &f.right;
+            quote!(#name = ::juniper::tracing::field::debug(#right))
+        });
+
+        args.chain(additional_fields).collect()
+    } else {
+        args.collect()
+    };
+
+    let level = field
+        .tracing_attr()
+        .map(|t| t.level.as_ref())
+        .flatten()
+        .map(|l| match l.value().as_str() {
+            "trace" => quote!(TRACE),
+            "debug" => quote!(DEBUG),
+            "info" => quote!(INFO),
+            "warn" => quote!(WARN),
+            "error" => quote!(ERROR),
+            l => abort!(syn::Error::new(
+                l.span(),
+                format!(
+                    "Unsupported tracing level: {}, \
+                     supported values: trace, debug, info, warn, error",
+                    l,
+                ),
+            )),
+        })
+        .unwrap_or_else(|| quote!(INFO));
+
+    let target = field
+        .tracing_attr()
+        .map(|t| t.target.as_ref())
+        .flatten()
+        .map_or_else(|| quote!(), |t| quote!(target: #t,));
+
+    quote!(
+        let _tracing_span = ::juniper::tracing::span!(
+            #target ::juniper::tracing::Level::#level, #span_name, #(#args, )*
+        );
+    )
+}
+
+// Returns code to start tracing of async future
+pub fn async_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field) {
+        return quote!();
+    }
+    quote!(.__instrument(_tracing_span))
+}
+
+// Returns code to start tracing of sync block
+pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field) {
+        return quote!();
+    }
+    quote!(let _tracing_guard = _tracing_span.enter();)
+}
+
+mod graphql_object {
+    use crate::util::{
+        GraphQLTypeDefinitionField, GraphQLTypeDefinitionFieldArg, GraphQLTypeDefiniton,
+    };
+
+    use super::{Attr, TracedArgument, TracedField, TracedType, TracingRule};
+
+    impl TracedType for GraphQLTypeDefiniton {
+        fn tracing_rule(&self) -> Option<TracingRule> {
+            self.tracing_rule
+        }
+
+        fn name(&self) -> &str {
+            self.name.as_str()
+        }
+
+        fn scalar(&self) -> Option<syn::Type> {
+            self.scalar.clone()
+        }
+    }
+
+    impl TracedField for GraphQLTypeDefinitionField {
+        type Arg = GraphQLTypeDefinitionFieldArg;
+
+        fn tracing_attr(&self) -> Option<&Attr> {
+            self.tracing.as_ref()
+        }
+
+        fn name(&self) -> &str {
+            self.name.as_str()
+        }
+
+        fn args(&self) -> Vec<&Self::Arg> {
+            self.args.iter().collect()
+        }
+
+        fn is_async(&self) -> bool {
+            self.is_async
+        }
+    }
+
+    impl TracedArgument for GraphQLTypeDefinitionFieldArg {
+        fn ty(&self) -> &syn::Type {
+            &self._type
+        }
+
+        fn name(&self) -> &str {
+            self.name.as_str()
+        }
+    }
+}

From 5b1fed82d636fbead8e8a8f54fcdc474e6ffd7bd Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 22 Jul 2021 12:25:36 +0300
Subject: [PATCH 54/72] Fully hide tracing behind feature gate, add docs

---
 docs/book/content/tracing/index.md            | 23 +++---
 juniper/Cargo.toml                            |  4 +-
 juniper/src/tests/fixtures/mod.rs             |  2 +-
 juniper/src/tests/fixtures/tracing/mod.rs     | 44 ++++++++++-
 juniper/src/tests/fixtures/tracing/schema.rs  | 26 +++----
 juniper/src/tests/mod.rs                      | 10 +--
 juniper/src/tests/trace_async_tests.rs        |  1 -
 juniper/src/tests/trace_sync_tests.rs         |  1 -
 .../{trace_tests.rs => tracing_tests.rs}      |  0
 juniper_codegen/Cargo.toml                    |  3 +-
 juniper_codegen/src/common/gen.rs             |  2 +
 juniper_codegen/src/derive_enum.rs            | 10 ++-
 juniper_codegen/src/derive_input_object.rs    |  7 +-
 juniper_codegen/src/derive_object.rs          |  7 +-
 juniper_codegen/src/graphql_interface/attr.rs |  8 +-
 juniper_codegen/src/graphql_interface/mod.rs  | 74 ++++++++++++-------
 .../src/graphql_interface/tracing.rs          |  6 +-
 juniper_codegen/src/impl_object.rs            |  9 ++-
 juniper_codegen/src/lib.rs                    | 19 +++++
 juniper_codegen/src/util/mod.rs               | 72 ++++++++----------
 juniper_codegen/src/util/tracing.rs           | 65 ++++++++++------
 rustfmt.toml                                  |  2 +-
 22 files changed, 236 insertions(+), 159 deletions(-)
 delete mode 100644 juniper/src/tests/trace_async_tests.rs
 delete mode 100644 juniper/src/tests/trace_sync_tests.rs
 rename juniper/src/tests/{trace_tests.rs => tracing_tests.rs} (100%)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 3d078e337..55989efcd 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -69,22 +69,19 @@ async fn main() {
 ```
 
 To trace how GraphQL object being resolved you need to enable one of tracing 
-features and use `tracing` argument on top-level `#[graphql_object]` and
-`#[graphql_interface]` attributes. `tracing` argument can be used with one of
-provided arguments:
-`"trace-sync"`, `"trace-async"`, `"trace-all"` and `"complex"`.
- - Use `trace-sync` to trace only synchronous part (struct fields and `fn`s).
- - Use `trace-async` to trace only asynchronous part (`async fn`s) and
+features and use `trace` argument on top-level `#[graphql_object]` and
+`#[graphql_interface]` attributes or `#[graphql]` if used with `#[derive(GraphQLObject)]`.
+`tracing` argument can be used with one of provided arguments:
+`"sync"`, `"async"`, `"skip-all"` and `"complex"`.
+ - Use `"sync"` to trace only synchronous part (struct fields and `fn`s).
+ - Use `"async"` to trace only asynchronous part (`async fn`s) and
 subscriptions.
- - Use `complex` to trace only fields marked with `#[tracing(complex)]`
- - Use `skip-all` to skip all fields.
+ - Use `"complex"` to trace only fields marked with `#[tracing(complex)]`
+ - Use `"skip-all"` to skip tracing of all fields.
 
 In addition you can use `#[tracing(no_trace)]` with all variants above to
 exclude field from tracing even if it belongs to traced group.
 
-**Note: `trace-sync` feature is mutually exclusive with `"trace-async"`
-argument and vice versa.**
-
 If resolving of certain field requires additional arguments (when used `fn`s or
 `async fn`s) they also will be included in resulted trace (except `self` and
 `Context`). You can use `skip` argument of `#[trace]` attribute, to skip some
@@ -124,9 +121,7 @@ In case above both `filter` and `count` will be recorded in [`Span`] for
 
 In most cases `#[tracing]` mimics behaviour of the `#[instrument]` attribute
 from [tracing] crate and you could use it as a reference. With the only key
-deference you should understand, it applied implicitly to all sync resolvers
-(including the derived ones) when the `trace-sync` feature is enabled, to
-all async resolvers when `trace-async` feature is enabled and to all if the
+deference you should understand, it applied implicitly to all resolvers if the
 `trace` feature is enabled.
 
 [tracing]: https://crates.io/crates/tracing
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index a7a54d8c8..710f71b33 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -30,9 +30,7 @@ expose-test-schema = ["anyhow", "serde_json"]
 graphql-parser-integration = ["graphql-parser"]
 scalar-naivetime = []
 schema-language = ["graphql-parser-integration"]
-trace = ["tracing", "tracing-futures", "juniper_codegen/trace-sync", "juniper_codegen/trace-async"]
-trace-async = ["tracing", "tracing-futures", "juniper_codegen/trace-async"]
-trace-sync = ["tracing", "tracing-futures", "juniper_codegen/trace-sync"]
+trace = ["tracing", "tracing-futures", "juniper_codegen/tracing"]
 
 [dependencies]
 juniper_codegen = { path = "../juniper_codegen" }
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index 799b6fed2..cdc9961d1 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -3,6 +3,6 @@
 /// GraphQL schema and data from Star Wars.
 pub mod starwars;
 
-#[cfg(any(feature = "trace", feature = "trace-sync", feature = "trace-async"))]
+#[cfg(feature = "trace")]
 /// Fixtures used to test integration with `tracing` crate.
 pub mod tracing;
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 315e440c4..925062aa8 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -1,4 +1,4 @@
-#![allow(missing_docs)]
+//! Fixtures used to test integration with `tracing` crate.
 
 pub mod schema;
 
@@ -11,30 +11,45 @@ use tracing::{
 };
 use tracing_core::{span, Subscriber};
 
+/// Information about `tracing` span recorded within tests.
 #[derive(Clone, Debug)]
-pub struct TestSpan {
+struct TestSpan {
     id: span::Id,
     fields: HashMap<String, String>,
     metadata: &'static Metadata<'static>,
 }
 
+/// Information about `tracing` event recorded within tests.
 #[derive(Clone, Debug)]
 pub struct TestEvent {
     fields: HashMap<String, String>,
     metadata: &'static Metadata<'static>,
 }
 
+/// Method calls on [`TestSubscriber`].
 #[derive(Clone, Debug)]
-pub enum SubscriberEvent {
+enum SubscriberEvent {
+    /// `new_span` method.
     NewSpan(TestSpan),
+
+    /// `enter` method.
     Enter(span::Id),
+
+    /// `exit` method.
     Exit(span::Id),
+
+    /// `clone_span` method.
     CloneSpan(span::Id),
+
+    /// `try_close` method.
     TryClose(span::Id),
+
+    /// `event` method.
     Event(TestEvent),
 }
 
 impl TestEvent {
+    /// Constructs new [`TestEvent`] from `tracing` [`Event`].
     pub fn new(ev: &Event<'_>) -> Self {
         let mut visitor = Visitor::new();
 
@@ -46,6 +61,7 @@ impl TestEvent {
     }
 }
 
+/// Simple visitor useful for converting `tracing` [`Event`]s into [`TestEvent`]s.
 struct Visitor(HashMap<String, String>);
 
 impl Visitor {
@@ -61,9 +77,13 @@ impl Visit for Visitor {
     }
 }
 
+/// Subscriber that logs every method call.
 #[derive(Clone)]
 pub struct TestSubscriber {
+    /// Counter used to create unique [`span::Id`]s.
     counter: Rc<RefCell<u64>>,
+
+    /// Log of method calls to this subscriber.
     events: Rc<RefCell<Vec<SubscriberEvent>>>,
 }
 
@@ -72,6 +92,7 @@ unsafe impl Sync for TestSubscriber {}
 unsafe impl Send for TestSubscriber {}
 
 impl TestSubscriber {
+    /// Returns new [`TestSubscriber`].
     pub fn new() -> Self {
         TestSubscriber {
             counter: Rc::new(RefCell::new(1)),
@@ -79,6 +100,7 @@ impl TestSubscriber {
         }
     }
 
+    /// Creates [`SubscriberAssert`] used to validated constructed spans.
     pub fn assert(self) -> SubscriberAssert {
         SubscriberAssert {
             name_to_span: HashMap::new(),
@@ -146,9 +168,10 @@ impl Subscriber for TestSubscriber {
     }
 }
 
+/// Wrapper representing span tree received from [`TestSubscriber`].
 pub struct SubscriberAssert {
     name_to_span: HashMap<span::Id, String>,
-    pub events: Vec<SubscriberEvent>,
+    events: Vec<SubscriberEvent>,
 }
 
 impl SubscriberAssert {
@@ -381,6 +404,7 @@ impl SubscriberAssert {
     }
 }
 
+/// Struct that can be compared to span recorded by [`TestSubscriber`].
 pub struct SpanLike {
     name: String,
     level: Option<Level>,
@@ -389,21 +413,27 @@ pub struct SpanLike {
     strict_fields: bool,
 }
 
+/// Abstraction over types that can be compared with span form `tracing` crate.
 pub trait AsSpan {
+    /// Name of span.
     fn name(&self) -> &str;
 
+    /// [`Level`] of span.
     fn level(&self) -> Option<Level> {
         None
     }
 
+    /// `target` of span.
     fn target(&self) -> Option<&str> {
         None
     }
 
+    /// `fields` recorded within a span.
     fn fields(&self) -> Vec<(String, String)> {
         vec![]
     }
 
+    /// Whether fields should be checked strictly.
     fn is_strict(&self) -> bool {
         false
     }
@@ -437,15 +467,21 @@ impl AsSpan for SpanLike {
     }
 }
 
+/// Extension that allows fluent construction of [`SpanLike`].
 pub trait SpanExt {
+    /// Sets the given `name`.
     fn with_name(self, name: &str) -> SpanLike;
 
+    /// Sets the given `level`.
     fn with_level(self, level: Level) -> SpanLike;
 
+    /// Sets the given `target`.
     fn with_target(self, target: &str) -> SpanLike;
 
+    /// Adds the given `field` with the `value`.
     fn with_field(self, field: &str, value: &str) -> SpanLike;
 
+    /// Sets `is_strict` to the given value.
     fn with_strict_fields(self, strict: bool) -> SpanLike;
 }
 
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 52f1fabba..7cde2aeb3 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -81,22 +81,22 @@ impl Query {
         ]
     }
 
-    /// Returns GraphQL object marked with `trace = "trace-async"`.
+    /// Returns GraphQL object marked with `trace = "async"`.
     async fn trace_async() -> TraceAsync {
         TraceAsync
     }
 
-    /// Returns derived GraphQL object marked with `trace = "trace-async"`.
+    /// Returns derived GraphQL object marked with `trace = "async"`.
     async fn derived_async() -> AsyncDerived {
         AsyncDerived::default()
     }
 
-    /// Returns GraphQL object marked with `trace = "trace-sync"`.
+    /// Returns GraphQL object marked with `trace = "sync"`.
     fn trace_sync() -> TraceSync {
         TraceSync
     }
 
-    /// Returns derived GraphQL object marked with `trace = "trace-sync"`.
+    /// Returns derived GraphQL object marked with `trace = "sync"`.
     fn derived_sync() -> SyncDerived {
         SyncDerived::default()
     }
@@ -135,12 +135,12 @@ impl Query {
         InterfacedSimpleValue::TraceSync(TraceSync)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "trace-sync"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "sync"`.
     fn erased_sync() -> InterfacedSyncValue {
         InterfacedSyncValue::TraceSync(TraceSync)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "trace-async"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "async"`.
     fn erased_async() -> InterfacedAsyncValue {
         InterfacedAsyncValue::TraceAsync(TraceAsync)
     }
@@ -350,7 +350,7 @@ pub trait FooBar {
 pub struct TraceSync;
 
 #[graphql_object(
-    trace = "trace-sync",
+    trace = "sync",
     impl = [InterfacedSimpleValue, InterfacedSyncValue],
 )]
 impl TraceSync {
@@ -363,9 +363,9 @@ impl TraceSync {
     }
 }
 
-/// Derived GraphQL object marked with `trace = "trace-sync"`.
+/// Derived GraphQL object marked with `trace = "sync"`.
 #[derive(Default, GraphQLObject)]
-#[graphql(trace = "trace-sync")]
+#[graphql(trace = "sync")]
 pub struct SyncDerived {
     /// Simple field
     sync: i32,
@@ -375,7 +375,7 @@ pub struct SyncDerived {
 pub struct TraceAsync;
 
 #[graphql_object(
-    trace = "trace-async",
+    trace = "async",
     impl = [InterfacedAsyncValue],
 )]
 impl TraceAsync {
@@ -390,7 +390,7 @@ impl TraceAsync {
 
 /// Derived GraphQL object
 #[derive(Default, GraphQLObject)]
-#[graphql(trace = "trace-async")]
+#[graphql(trace = "async")]
 pub struct AsyncDerived {
     /// Simple field
     sync: i32,
@@ -471,7 +471,7 @@ trait InterfacedSimple {
 
 #[graphql_interface(
     for = [TraceSync],
-    trace = "trace-sync",
+    trace = "sync",
     async,
 )]
 trait InterfacedSync {
@@ -481,7 +481,7 @@ trait InterfacedSync {
 
 #[graphql_interface(
     for = [TraceAsync],
-    trace = "trace-async",
+    trace = "async",
     async,
 )]
 trait InterfacedAsync {
diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index b144975f0..80d3485fc 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -14,12 +14,4 @@ mod type_info_tests;
 
 #[cfg(test)]
 #[cfg(feature = "trace")]
-mod trace_tests;
-
-#[cfg(test)]
-#[cfg(feature = "trace-sync")]
-mod trace_sync_tests;
-
-#[cfg(test)]
-#[cfg(feature = "trace-async")]
-mod trace_async_tests;
+mod tracing_tests;
diff --git a/juniper/src/tests/trace_async_tests.rs b/juniper/src/tests/trace_async_tests.rs
deleted file mode 100644
index 8b1378917..000000000
--- a/juniper/src/tests/trace_async_tests.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/juniper/src/tests/trace_sync_tests.rs b/juniper/src/tests/trace_sync_tests.rs
deleted file mode 100644
index 8b1378917..000000000
--- a/juniper/src/tests/trace_sync_tests.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/juniper/src/tests/trace_tests.rs b/juniper/src/tests/tracing_tests.rs
similarity index 100%
rename from juniper/src/tests/trace_tests.rs
rename to juniper/src/tests/tracing_tests.rs
diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml
index 47642bd30..c7c136c9d 100644
--- a/juniper_codegen/Cargo.toml
+++ b/juniper_codegen/Cargo.toml
@@ -18,8 +18,7 @@ travis-ci = { repository = "graphql-rust/juniper" }
 proc-macro = true
 
 [features]
-trace-async = []
-trace-sync = []
+tracing = []
 
 [dependencies]
 proc-macro-error = "1.0.2"
diff --git a/juniper_codegen/src/common/gen.rs b/juniper_codegen/src/common/gen.rs
index 2a549536a..52e1d8535 100644
--- a/juniper_codegen/src/common/gen.rs
+++ b/juniper_codegen/src/common/gen.rs
@@ -27,6 +27,8 @@ pub(crate) fn sync_resolving_code() -> TokenStream {
 /// Optional `ty` argument may be used to annotate a concrete type of the resolving
 /// [GraphQL type][1] (the [`Future::Output`]).
 ///
+/// `trace_async` may be used to pass extension to trace code execution.
+///
 /// [`Future`]: std::future::Future
 /// [`Future::Output`]: std::future::Future::Output
 /// [1]: https://spec.graphql.org/June2018/#sec-Types
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index 7abf394e6..273568e66 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -94,12 +94,14 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: field_attrs.deprecation.map(SpanContainer::into_inner),
-                tracing: field_attrs.tracing,
                 resolver_code,
                 is_type_inferred: true,
                 is_async: false,
                 default: None,
                 span,
+
+                #[cfg(feature = "tracing")]
+                tracing: field_attrs.tracing,
             })
         })
         .collect::<Vec<_>>();
@@ -135,7 +137,7 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
 
     proc_macro_error::abort_if_dirty();
 
-    let definition = util::GraphQLTypeDefiniton {
+    let definition = util::GraphQLTypeDefinition {
         name,
         _type: syn::parse_str(&ast.ident.to_string()).unwrap(),
         context: attrs.context.map(SpanContainer::into_inner),
@@ -149,7 +151,9 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
 
-        tracing_rule: attrs.tracing_rule,
+        // NOTICE: nothing to trace on enums
+        #[cfg(feature = "tracing")]
+        tracing_rule: None,
     };
 
     Ok(definition.into_enum_tokens())
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index c10b580fb..a3f4822ce 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -90,12 +90,14 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: None,
-                tracing: field_attrs.tracing,
                 resolver_code,
                 is_type_inferred: true,
                 is_async: false,
                 default,
                 span,
+
+                #[cfg(feature = "tracing")]
+                tracing: field_attrs.tracing,
             })
         })
         .collect::<Vec<_>>();
@@ -134,7 +136,7 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
 
     proc_macro_error::abort_if_dirty();
 
-    let definition = util::GraphQLTypeDefiniton {
+    let definition = util::GraphQLTypeDefinition {
         name,
         _type: syn::parse_str(&ast.ident.to_string()).unwrap(),
         context: attrs.context.map(SpanContainer::into_inner),
@@ -147,6 +149,7 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
 
+        #[cfg(feature = "tracing")]
         tracing_rule: attrs.tracing_rule,
     };
 
diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs
index fed1ff57b..8d53c5d6c 100644
--- a/juniper_codegen/src/derive_object.rs
+++ b/juniper_codegen/src/derive_object.rs
@@ -82,12 +82,14 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
                 args: Vec::new(),
                 description: field_attrs.description.map(SpanContainer::into_inner),
                 deprecation: field_attrs.deprecation.map(SpanContainer::into_inner),
-                tracing: field_attrs.tracing,
                 resolver_code,
                 default: None,
                 is_type_inferred: true,
                 is_async: false,
                 span,
+
+                #[cfg(feature = "tracing")]
+                tracing: field_attrs.tracing,
             })
         })
         .collect::<Vec<_>>();
@@ -116,7 +118,7 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
     // Early abort after GraphQL properties
     proc_macro_error::abort_if_dirty();
 
-    let definition = util::GraphQLTypeDefiniton {
+    let definition = util::GraphQLTypeDefinition {
         name,
         _type: syn::parse_str(&ast.ident.to_string()).unwrap(),
         context: attrs.context.map(SpanContainer::into_inner),
@@ -133,6 +135,7 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
 
+        #[cfg(feature = "tracing")]
         tracing_rule: attrs.tracing_rule,
     };
 
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 66f40bdc8..13c8f974a 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -6,13 +6,15 @@ use proc_macro2::{Span, TokenStream};
 use quote::{quote, ToTokens as _};
 use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned};
 
+#[cfg(feature = "tracing")]
+use crate::util::tracing;
 use crate::{
     common::{
         parse::{self, TypeExt as _},
         ScalarValueType,
     },
     result::GraphQLScope,
-    util::{path_eq_single, span_container::SpanContainer, to_camel_case, tracing},
+    util::{path_eq_single, span_container::SpanContainer, to_camel_case},
 };
 
 use super::{
@@ -211,7 +213,8 @@ pub fn expand_on_trait(
         fields,
         implementers,
 
-        tracing_rule: meta.tracing.map(|t| *t.inner()),
+        #[cfg(feature = "tracing")]
+        tracing_rule: meta.tracing_rule.map(|t| *t.inner()),
     };
 
     // Attach the `juniper::AsDynGraphQLValue` on top of the trait if dynamic dispatch is used.
@@ -504,6 +507,7 @@ impl TraitMethod {
             arguments,
             is_async: method.sig.asyncness.is_some(),
 
+            #[cfg(feature = "tracing")]
             tracing: tracing::Attr::from_method(method),
         })
     }
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index 33ff96b43..a06bbd4b5 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -3,15 +3,12 @@
 //! [1]: https://spec.graphql.org/June2018/#sec-Interfaces
 
 pub mod attr;
+#[cfg(feature = "tracing")]
 mod tracing;
 
-use std::{
-    collections::{HashMap, HashSet},
-    str::FromStr,
-};
+use std::collections::{HashMap, HashSet};
 
 use proc_macro2::TokenStream;
-use proc_macro_error::abort;
 use quote::{format_ident, quote, ToTokens, TokenStreamExt as _};
 use syn::{
     parse::{Parse, ParseStream},
@@ -125,7 +122,16 @@ struct TraitMeta {
     /// library.
     is_internal: bool,
 
-    tracing: Option<SpanContainer<tracing::TracingRule>>,
+    /// Explicitly specified rule for tracing of fields that belong to this [GraphQL interface][1].
+    ///
+    /// If absent and `tracing` feature enabled all fields not marked with `#[tracing(no_trace)]`
+    /// will be traced.
+    ///
+    /// If it present but feature `tracing` disabled it will cause compilation error.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
+    #[cfg(feature = "tracing")]
+    tracing_rule: Option<SpanContainer<tracing::Rule>>,
 }
 
 impl Parse for TraitMeta {
@@ -224,21 +230,26 @@ impl Parse for TraitMeta {
                 "internal" => {
                     output.is_internal = true;
                 }
+                #[cfg(feature = "tracing")]
                 "trace" => {
+                    use std::str::FromStr as _;
+
+                    use proc_macro_error::abort;
+
                     let span = ident.span();
                     input.parse::<token::Eq>()?;
                     let tracing = input.parse::<syn::LitStr>()?;
-                    let tracing_rule = tracing::TracingRule::from_str(tracing.value().as_str());
+                    let tracing_rule = tracing::Rule::from_str(tracing.value().as_str());
                     match tracing_rule {
                         Ok(rule) => output
-                            .tracing
+                            .tracing_rule
                             .replace(SpanContainer::new(span, Some(tracing.span()), rule))
                             .none_or_else(|_| err::dup_arg(span))?,
                         Err(_) => abort!(syn::Error::new(
                             tracing.span(),
                             format!(
                                 "Unknown tracing rule: {}, \
-                             known values: trace-sync, trace-async, skip-all and complex",
+                                 known values: trace-sync, trace-async, skip-all and complex",
                                 tracing.value(),
                             )
                         )),
@@ -271,7 +282,8 @@ impl TraitMeta {
                 external_downcasts: self, another => span_joined
             ),
             is_internal: self.is_internal || another.is_internal,
-            tracing: try_merge_opt!(tracing: self, another),
+            #[cfg(feature = "tracing")]
+            tracing_rule: try_merge_opt!(tracing_rule: self, another),
         })
     }
 
@@ -780,7 +792,13 @@ struct Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     implementers: Vec<Implementer>,
 
-    tracing_rule: Option<tracing::TracingRule>,
+    /// Explicitly defined rules of [GraphQL field][2]s that belongs to this
+    /// [GraphQL interface][1].
+    ///
+    /// If it's absent and `tracing` feature is enabled all [field][2]s not marked
+    /// with `#[tracing(no_trace)]` will be traced.
+    #[cfg(feature = "tracing")]
+    tracing_rule: Option<tracing::Rule>,
 }
 
 impl Definition {
@@ -998,7 +1016,7 @@ impl Definition {
             .filter_map(|i| i.method_resolve_into_type_async_tokens(&trait_ty));
         let regular_downcast = self.ty.method_resolve_into_type_async_tokens();
 
-        let instrument = tracing::instrument();
+        let instrument = if_tracing_enabled!(tracing::instrument());
 
         quote! {
             #[automatically_derived]
@@ -1307,6 +1325,16 @@ struct Field {
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     is_async: bool,
 
+    /// Tracing attribute placed on this [GraphQL field][2]. Only relevant when `tracing`
+    /// feature is enabled.
+    ///
+    /// If it is present and `tracing` feature is enabled it can be used to alter trace
+    /// behaviour.
+    ///
+    /// It it is present and `tracing` feature is disabled it will cause compile time error.
+    ///
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    #[cfg(feature = "tracing")]
     tracing: Option<tracing::Attr>,
 }
 
@@ -1353,6 +1381,7 @@ impl Field {
     ///
     /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
     #[must_use]
+    #[allow(unused_variables)]
     fn method_resolve_field_tokens(
         &self,
         trait_ty: &syn::Type,
@@ -1371,19 +1400,14 @@ impl Field {
 
         let resolving_code = gen::sync_resolving_code();
 
-        let (tracing_span, trace_sync) = if cfg!(feature = "trace-sync") {
-            (
-                tracing::span_tokens(definition, self),
-                tracing::sync_tokens(definition, self),
-            )
-        } else {
-            (quote!(), quote!())
-        };
+        let tracing_span = if_tracing_enabled!(tracing::span_tokens(definition, self));
+        let trace_sync = if_tracing_enabled!(tracing::sync_tokens(definition, self));
 
         Some(quote! {
             #name => {
                 #tracing_span
                 #trace_sync
+
                 let res: #ty = <Self as #trait_ty>::#method(self #( , #arguments )*);
                 #resolving_code
             }
@@ -1412,14 +1436,8 @@ impl Field {
             fut = quote! { ::juniper::futures::future::ready(#fut) };
         }
 
-        let (tracing_span, trace_async) = if cfg!(feature = "trace-async") {
-            (
-                tracing::span_tokens(definition, self),
-                tracing::async_tokens(definition, self),
-            )
-        } else {
-            (quote!(), quote!())
-        };
+        let tracing_span = if_tracing_enabled!(tracing::span_tokens(definition, self));
+        let trace_async = if_tracing_enabled!(tracing::async_tokens(definition, self));
 
         let resolving_code = gen::async_resolving_code(Some(ty), trace_async);
 
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
index 4f9403f8d..1d3c0c440 100644
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -1,12 +1,12 @@
 pub use crate::util::tracing::{
-    async_tokens, instrument, span_tokens, sync_tokens, Attr, TracedArgument, TracedField,
-    TracedType, TracingRule,
+    async_tokens, instrument, span_tokens, sync_tokens, Attr, Rule, TracedArgument, TracedField,
+    TracedType,
 };
 
 use super::{Definition, Field, FieldArgument};
 
 impl TracedType for Definition {
-    fn tracing_rule(&self) -> Option<TracingRule> {
+    fn tracing_rule(&self) -> Option<Rule> {
         self.tracing_rule
     }
 
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 916e4f534..705ceb791 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -34,7 +34,7 @@ fn create(
     args: TokenStream,
     body: TokenStream,
     error: GraphQLScope,
-) -> syn::Result<util::GraphQLTypeDefiniton> {
+) -> syn::Result<util::GraphQLTypeDefinition> {
     let body_span = body.span();
     let _impl = util::parse_impl::ImplBlock::parse(args, body)?;
     let name = _impl
@@ -175,12 +175,14 @@ fn create(
                 args,
                 description: attrs.description.map(SpanContainer::into_inner),
                 deprecation: attrs.deprecation.map(SpanContainer::into_inner),
-                tracing: attrs.tracing,
                 resolver_code,
                 is_type_inferred: false,
                 is_async,
                 default: None,
                 span,
+
+                #[cfg(feature = "tracing")]
+                tracing: attrs.tracing,
             })
         })
         .collect::<Vec<_>>();
@@ -209,7 +211,7 @@ fn create(
     // Early abort after GraphQL properties
     proc_macro_error::abort_if_dirty();
 
-    let definition = util::GraphQLTypeDefiniton {
+    let definition = util::GraphQLTypeDefinition {
         name,
         _type: *_impl.target_type.clone(),
         scalar: _impl.attrs.scalar.map(SpanContainer::into_inner),
@@ -227,6 +229,7 @@ fn create(
         generic_scalar: true,
         no_async: _impl.attrs.no_async.is_some(),
 
+        #[cfg(feature = "tracing")]
         tracing_rule: _impl.attrs.tracing_rule,
     };
 
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index 7e1196709..605a7839f 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -7,6 +7,25 @@
 #![doc(html_root_url = "https://docs.rs/juniper_codegen/0.15.7")]
 #![recursion_limit = "1024"]
 
+// NOTICE: Unfortunately this macro MUST be defined here, in the crate's root module, because Rust
+//         doesn't allow to export `macro_rules!` macros from a `proc-macro` crate type currently,
+//         and so we cannot move the definition into a sub-module and use the `#[macro_export]`
+//         attribute. Also it should be declared before `util` mod because `util` relies on it.
+/// Expands to `$expr` if `tracing` feature of this crate is enabled, otherwise returns an
+/// empty [`TokenStream`].
+macro_rules! if_tracing_enabled {
+    ($code: expr) => {{
+        #[cfg(feature = "tracing")]
+        {
+            $code
+        }
+        #[cfg(not(feature = "tracing"))]
+        {
+            ::quote::quote!()
+        }
+    }};
+}
+
 mod result;
 mod util;
 
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index a5cd850bc..133991c89 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -3,6 +3,8 @@
 pub mod duplicate;
 pub mod parse_impl;
 pub mod span_container;
+
+#[cfg(feature = "tracing")]
 pub mod tracing;
 
 use std::{collections::HashMap, str::FromStr};
@@ -19,7 +21,6 @@ use syn::{
     spanned::Spanned,
     token, Attribute, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
 };
-use tracing::{Attr, TracingRule};
 
 use crate::common::parse::ParseBufferExt as _;
 
@@ -341,7 +342,8 @@ pub struct ObjectAttributes {
     pub is_internal: bool,
     pub rename: Option<RenameRule>,
 
-    pub tracing_rule: Option<TracingRule>,
+    #[cfg(feature = "tracing")]
+    pub tracing_rule: Option<tracing::Rule>,
 }
 
 impl Parse for ObjectAttributes {
@@ -412,10 +414,11 @@ impl Parse for ObjectAttributes {
                         return Err(syn::Error::new(val.span(), "unknown rename rule"));
                     }
                 }
+                #[cfg(feature = "tracing")]
                 "trace" => {
                     input.parse::<syn::token::Eq>()?;
                     let val = input.parse::<syn::LitStr>()?;
-                    if let Ok(trace) = TracingRule::from_str(&val.value()) {
+                    if let Ok(trace) = tracing::Rule::from_str(&val.value()) {
                         output.tracing_rule = Some(trace);
                     } else {
                         return Err(syn::Error::new(val.span(), "unknown tracing skip rule"));
@@ -604,7 +607,8 @@ pub struct FieldAttributes {
     /// Only relevant for object input objects.
     pub default: Option<SpanContainer<Option<syn::Expr>>>,
     // Only relevant when `tracing` feature enabled
-    pub tracing: Option<Attr>,
+    #[cfg(feature = "tracing")]
+    pub tracing: Option<tracing::Attr>,
 }
 
 impl Parse for FieldAttributes {
@@ -667,9 +671,8 @@ impl FieldAttributes {
             output.deprecation = deprecation;
         }
 
-        let tracing_attr = attrs.iter().find(|attr| attr.path.is_ident("tracing"));
-
-        if let Some(attr) = tracing_attr {
+        #[cfg(feature = "tracing")]
+        if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("tracing")) {
             output.tracing = Some(attr.parse_args()?);
         }
 
@@ -695,13 +698,15 @@ pub struct GraphQLTypeDefinitionField {
     pub _type: syn::Type,
     pub description: Option<String>,
     pub deprecation: Option<DeprecationAttr>,
-    pub tracing: Option<Attr>,
     pub args: Vec<GraphQLTypeDefinitionFieldArg>,
     pub resolver_code: TokenStream,
     pub is_type_inferred: bool,
     pub is_async: bool,
     pub default: Option<TokenStream>,
     pub span: Span,
+
+    #[cfg(feature = "tracing")]
+    pub tracing: Option<tracing::Attr>,
 }
 
 impl syn::spanned::Spanned for GraphQLTypeDefinitionField {
@@ -720,7 +725,7 @@ impl<'a> syn::spanned::Spanned for &'a GraphQLTypeDefinitionField {
 /// by various macros.
 /// The definition can be rendered to Rust code.
 #[derive(Debug)]
-pub struct GraphQLTypeDefiniton {
+pub struct GraphQLTypeDefinition {
     pub name: String,
     pub _type: syn::Type,
     pub context: Option<syn::Type>,
@@ -744,10 +749,11 @@ pub struct GraphQLTypeDefiniton {
     // FIXME: make this redundant.
     pub no_async: bool,
 
-    pub tracing_rule: Option<TracingRule>,
+    #[cfg(feature = "tracing")]
+    pub tracing_rule: Option<tracing::Rule>,
 }
 
-impl GraphQLTypeDefiniton {
+impl GraphQLTypeDefinition {
     #[allow(unused)]
     fn has_async_field(&self) -> bool {
         self.fields.iter().any(|field| field.is_async)
@@ -845,14 +851,8 @@ impl GraphQLTypeDefiniton {
                     },
                 )
             } else {
-                let (tracing_span, trace_sync) = if cfg!(feature = "trace-sync") {
-                    (
-                        tracing::span_tokens(&self, field),
-                        tracing::sync_tokens(&self, field),
-                    )
-                } else {
-                    (quote!(), quote!())
-                };
+                let tracing_span = if_tracing_enabled!(tracing::span_tokens(&self, field));
+                let trace_sync = if_tracing_enabled!(tracing::sync_tokens(&self, field));
 
                 let _type = if field.is_type_inferred {
                     quote!()
@@ -864,6 +864,7 @@ impl GraphQLTypeDefiniton {
                     #name => {
                         #tracing_span
                         #trace_sync
+
                         let res #_type = (|| { #code })();
                         ::juniper::IntoResolvable::into(
                             res,
@@ -921,7 +922,7 @@ impl GraphQLTypeDefiniton {
         };
         let (impl_generics, _, where_clause) = generics.split_for_impl();
 
-        let tracing_instrument = tracing::instrument();
+        let tracing_instrument = if_tracing_enabled!(tracing::instrument());
 
         let resolve_field_async = {
             let resolve_matches_async = self.fields.iter().map(|field| {
@@ -934,13 +935,9 @@ impl GraphQLTypeDefiniton {
                     quote!(: #_type)
                 };
 
-                let tracing_span = tracing::span_tokens(&self, field);
+                let tracing_span = if_tracing_enabled!(tracing::span_tokens(&self, field));
                 if field.is_async {
-                    let trace_async = if cfg!(feature = "trace-async") {
-                        tracing::async_tokens(&self, field)
-                    } else {
-                        quote!()
-                    };
+                    let trace_async = if_tracing_enabled!(tracing::async_tokens(&self, field));
 
                     quote!(
                         #name => {
@@ -967,14 +964,8 @@ impl GraphQLTypeDefiniton {
                         },
                     )
                 } else {
-                    let (trace_async, trace_sync) = if cfg!(feature = "trace-sync") {
-                        (
-                            tracing::async_tokens(&self, field),
-                            tracing::sync_tokens(&self, field),
-                        )
-                    } else {
-                        (quote!(), quote!())
-                    };
+                    let trace_async = if_tracing_enabled!(tracing::async_tokens(&self, field));
+                    let trace_sync = if_tracing_enabled!(tracing::sync_tokens(&self, field));
 
                     let inner = if !self.no_async {
                         quote!(
@@ -1331,17 +1322,12 @@ impl GraphQLTypeDefiniton {
                     _type = quote!(: #_type_name);
                 };
 
-                let (trace_span, trace_async) = if cfg!(feature = "trace-async") {
-                    (
-                        tracing::span_tokens(&self, field),
-                        tracing::async_tokens(&self, field),
-                    )
-                } else {
-                    (quote!(), quote!())
-                };
+                let trace_span = if_tracing_enabled!(tracing::span_tokens(&self, field));
+                let trace_async = if_tracing_enabled!(tracing::async_tokens(&self, field));
                 quote!(
                     #name => {
                         #trace_span
+
                         ::juniper::futures::FutureExt::boxed(async move {
                             let res #_type = async { #code }.await;
                             let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
@@ -1456,7 +1442,7 @@ impl GraphQLTypeDefiniton {
             }
         );
 
-        let instrument = tracing::instrument();
+        let instrument = if_tracing_enabled!(tracing::instrument());
 
         let subscription_implementation = quote!(
             impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens
diff --git a/juniper_codegen/src/util/tracing.rs b/juniper_codegen/src/util/tracing.rs
index bf420c817..0875d1650 100644
--- a/juniper_codegen/src/util/tracing.rs
+++ b/juniper_codegen/src/util/tracing.rs
@@ -11,12 +11,25 @@ use syn::{
 
 #[derive(Debug, Default)]
 pub struct Attr {
+    /// Optional span rename, if `None` method name should be used instead.
     name: Option<syn::LitStr>,
+
+    /// Overwritten `level` of span generated, if `None` `Level::INFO` should be used.
     level: Option<syn::LitStr>,
+
+    /// Overwritten `target` of span.
     target: Option<syn::LitStr>,
+
+    /// Skipped arguments on `fn` resolvers.
     skip: HashMap<String, syn::Ident>,
+
+    /// Custom fields.
     fields: Vec<syn::ExprAssign>,
+
+    /// Whether this field is marked with `#[tracing(complex)]`
     is_complex: bool,
+
+    /// Whether this field is marked with `#[tracing(no_trace)]`
     no_trace: bool,
 }
 
@@ -100,11 +113,11 @@ impl Parse for Attr {
 
 /// The different possible groups of fields to trace.
 #[derive(Copy, Clone, Debug)]
-pub enum TracingRule {
+pub enum Rule {
     /// Trace all fields that resolved using `async fn`s.
     Async,
 
-    /// Trace all fields that can be syncronously resolved.
+    /// Trace all fields that can be synchronously resolved.
     Sync,
 
     /// Trace only fields that marked with `#[tracing(complex)]`.
@@ -114,8 +127,7 @@ pub enum TracingRule {
     SkipAll,
 }
 
-impl TracingRule {
-    #[cfg(any(feature = "trace-async", feature = "trace-sync"))]
+impl Rule {
     pub fn is_traced(&self, field: &impl TracedField) -> bool {
         match self {
             Self::Async => field.is_async(),
@@ -126,13 +138,13 @@ impl TracingRule {
     }
 }
 
-impl FromStr for TracingRule {
+impl FromStr for Rule {
     type Err = ();
 
     fn from_str(rule: &str) -> Result<Self, Self::Err> {
         match rule {
-            "trace-async" => Ok(Self::Async),
-            "trace-sync" => Ok(Self::Sync),
+            "async" => Ok(Self::Async),
+            "sync" => Ok(Self::Sync),
             "skip-all" => Ok(Self::SkipAll),
             "complex" => Ok(Self::Complex),
             _ => Err(()),
@@ -140,18 +152,35 @@ impl FromStr for TracingRule {
     }
 }
 
+/// Generalisation of type that can be traced.
 pub trait TracedType {
-    fn tracing_rule(&self) -> Option<TracingRule>;
+    /// Optional [`TracingRule`] read from attributes `#[graphql_object(trace = "...")]`
+    /// on impl block, `#[graphql(trace = "...")]` on derived GraphQLObject or
+    /// `#[graphql_interface(trace = "...")]` on trait definition.
+    fn tracing_rule(&self) -> Option<Rule>;
+
+    /// Name of this type.
     fn name(&self) -> &str;
+
+    /// Scalar used by this GraphQL object.
     fn scalar(&self) -> Option<syn::Type>;
 }
 
+/// Trait that marks type that this is field that can be traced.
 pub trait TracedField {
+    /// Type of argument used by this field.
     type Arg: TracedArgument;
 
+    /// Returns parsed `#[tracing]` attribute.
     fn tracing_attr(&self) -> Option<&Attr>;
+
+    /// Whether this field relies on async resolver.
     fn is_async(&self) -> bool;
+
+    /// Name of this field.
     fn name(&self) -> &str;
+
+    /// Arguments if resolver is `fn`.
     fn args(&self) -> Vec<&Self::Arg>;
 }
 
@@ -160,7 +189,6 @@ pub trait TracedArgument {
     fn name(&self) -> &str;
 }
 
-#[cfg(any(feature = "trace-async", feature = "trace-sync"))]
 fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
     let traced = ty
         .tracing_rule()
@@ -171,23 +199,12 @@ fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
     traced && !no_trace
 }
 
-#[cfg(not(any(feature = "trace-async", feature = "trace-sync")))]
-fn is_traced(_: &impl TracedType, _: &impl TracedField) -> bool {
-    false
-}
-
-#[cfg(any(feature = "trace-async", feature = "trace-sync"))]
 pub fn instrument() -> TokenStream {
     quote!(
         use ::juniper::InstrumentInternal as _;
     )
 }
 
-#[cfg(not(any(feature = "trace-async", feature = "trace-sync")))]
-pub fn instrument() -> TokenStream {
-    quote!()
-}
-
 // Returns code that constructs `span` required for tracing
 pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
@@ -287,13 +304,13 @@ pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
 
 mod graphql_object {
     use crate::util::{
-        GraphQLTypeDefinitionField, GraphQLTypeDefinitionFieldArg, GraphQLTypeDefiniton,
+        GraphQLTypeDefinition, GraphQLTypeDefinitionField, GraphQLTypeDefinitionFieldArg,
     };
 
-    use super::{Attr, TracedArgument, TracedField, TracedType, TracingRule};
+    use super::{Attr, Rule, TracedArgument, TracedField, TracedType};
 
-    impl TracedType for GraphQLTypeDefiniton {
-        fn tracing_rule(&self) -> Option<TracingRule> {
+    impl TracedType for GraphQLTypeDefinition {
+        fn tracing_rule(&self) -> Option<Rule> {
             self.tracing_rule
         }
 
diff --git a/rustfmt.toml b/rustfmt.toml
index dd541e194..00146874d 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,2 +1,2 @@
 imports_granularity = "Crate"
-use_field_init_shorthand = true
\ No newline at end of file
+use_field_init_shorthand = true

From 05a1432317ecc3d6656dfab423773b77ce0b1f96 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 10:28:22 +0300
Subject: [PATCH 55/72] Add test for subscriptions

---
 juniper/CHANGELOG.md                          |   2 +-
 juniper/Cargo.toml                            |   4 +-
 juniper/src/tests/fixtures/mod.rs             |   2 +-
 juniper/src/tests/fixtures/tracing/schema.rs  |  12 ++
 juniper/src/tests/tracing_tests.rs            | 145 +++++++++++++-----
 juniper_codegen/src/graphql_interface/attr.rs |  11 +-
 juniper_codegen/src/graphql_interface/mod.rs  |  47 ++++--
 .../src/graphql_interface/tracing.rs          |   4 +
 juniper_codegen/src/impl_object.rs            |  15 +-
 juniper_codegen/src/util/mod.rs               |  23 ++-
 juniper_codegen/src/util/tracing.rs           |  17 +-
 11 files changed, 208 insertions(+), 74 deletions(-)

diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index b16d37038..5b7e53aaa 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -1,6 +1,6 @@
 # master
 
-- Fix fields on interfaces not being resolved when used with fragments ([#923](https://github.com/graphql-rust/juniper/pull/923))
+- Add optional `tracing` integration ([#972](https://github.com/graphql-rust/juniper/pull/972))
 
 # [[0.15.7] 2021-07-08](https://github.com/graphql-rust/juniper/releases/tag/juniper-v0.15.7)
 
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 710f71b33..106cf2b93 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -54,14 +54,14 @@ uuid = { version = "0.8", default-features = false, optional = true }
 
 tracing = { version = "0.1.23", optional = true }
 tracing-futures = { version = "0.2.5", optional = true, features = ["futures-03"] }
-tracing-core = "0.1"
-tracing-subscriber = "0.2"
 
 [dev-dependencies]
 bencher = "0.1.2"
 pretty_assertions = "0.7.1"
 serde_json = "1.0.2"
 tokio = { version = "1", features = ["macros", "time", "rt-multi-thread"] }
+tracing-core = { version = "0.1" }
+tracing-subscriber = "0.2"
 
 [[bench]]
 name = "bench"
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index cdc9961d1..090c8c308 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -3,6 +3,6 @@
 /// GraphQL schema and data from Star Wars.
 pub mod starwars;
 
-#[cfg(feature = "trace")]
 /// Fixtures used to test integration with `tracing` crate.
+#[cfg(feature = "trace")]
 pub mod tracing;
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 7cde2aeb3..3c933c150 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -2,6 +2,7 @@
 
 use std::collections::HashMap;
 
+use futures::stream::{self, BoxStream};
 use tracing::instrument;
 
 use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLObject};
@@ -156,6 +157,17 @@ impl Query {
     }
 }
 
+pub struct Subscriptions;
+
+#[graphql_subscription(context = Database)]
+impl Subscriptions {
+    async fn bar_sub(id: i32) -> BoxStream<'static, Bar> {
+        let items = [Bar { id: id + 1 }, Bar { id: id + 2 }];
+
+        stream::iter(items).boxed()
+    }
+}
+
 /// Simple GraphQL object.
 pub struct Foo {
     id: i32,
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index dfe5dfbde..a38724db2 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -1,13 +1,14 @@
+use futures::StreamExt as _;
 use tracing::Level;
 
 use crate::{
     executor::Variables,
     schema::model::RootNode,
     tests::fixtures::tracing::{
-        schema::{Database, Query},
+        schema::{Database, Query, Subscriptions},
         SpanExt as _, TestSubscriber,
     },
-    types::scalars::{EmptyMutation, EmptySubscription},
+    types::scalars::EmptyMutation,
 };
 
 #[test]
@@ -21,11 +22,7 @@ fn test_execute_sync_clean() {
             }
         }"#;
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let collector = TestSubscriber::new();
     let handle = collector.clone();
@@ -61,11 +58,7 @@ fn test_execute_sync_with_error() {
             }
         }"#;
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -104,11 +97,7 @@ async fn test_no_trace_field() {
             }
         }"#;
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -152,11 +141,7 @@ async fn test_impl_fn_args() {
             }
         }"#;
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -209,11 +194,7 @@ async fn test_custom_fields() {
         }
     "#;
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -271,11 +252,7 @@ async fn overwrite_level_and_target() {
         "#;
 
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -384,11 +361,7 @@ async fn graphql_object_trace_arg() {
         "#;
 
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -467,11 +440,7 @@ async fn graphql_interface_trace_arg() {
         "#;
 
     let database = Database::new();
-    let schema = RootNode::new(
-        Query,
-        EmptyMutation::<Database>::new(),
-        EmptySubscription::<Database>::new(),
-    );
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
 
     let subscriber = TestSubscriber::new();
     let handle = subscriber.clone();
@@ -505,3 +474,95 @@ async fn graphql_interface_trace_arg() {
         .close_exited("execute_validated_query")
         .close_exited("execute");
 }
+
+#[tokio::test]
+async fn subscription_tracing() {
+    let doc = r#"
+        subscription {
+            barSub(id: 10) {
+                id
+                defaultArg(another: -1)
+            }
+        }
+        "#;
+
+    let database = Database::new();
+    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
+
+    let subscriber = TestSubscriber::new();
+    let handle = subscriber.clone();
+
+    let _guard = tracing::subscriber::set_default(subscriber);
+
+    let request = crate::http::GraphQLRequest::new(doc.to_owned(), None, None);
+
+    let response = crate::http::resolve_into_stream(&request, &schema, &database).await;
+
+    assert!(response.is_ok());
+
+    let (values, errors) = response.unwrap();
+
+    assert_eq!(errors.len(), 0, "Should return no errors");
+
+    // cannot compare with `assert_eq` because
+    // stream does not implement Debug
+    let response_value_object = match values {
+        juniper::Value::Object(o) => Some(o),
+        _ => None,
+    };
+
+    assert!(response_value_object.is_some());
+
+    let response_returned_object = response_value_object.unwrap();
+
+    let fields = response_returned_object.into_iter();
+
+    let mut names = vec![];
+    let mut collected_values = vec![];
+
+    for (name, stream_val) in fields {
+        names.push(name.clone());
+
+        // since macro returns Value::Scalar(iterator) every time,
+        // other variants may be skipped
+        match stream_val {
+            juniper::Value::Scalar(stream) => {
+                let collected = stream.collect::<Vec<_>>().await;
+                collected_values.push(collected);
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    // Required argument
+    handle
+        .assert()
+        .enter_new_span("resolve_into_stream")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("resolve_validated_subscription")
+        .new_span(
+            &"Subscriptions.barSub"
+                .with_field("id", "10")
+                .with_strict_fields(true),
+        )
+        .close_exited("resolve_validated_subscription")
+        .close_exited("resolve_into_stream")
+        .enter("Subscriptions.barSub")
+        .simple_span(&"Bar.id".with_field("self.id", "11"))
+        .simple_span(
+            &"Bar.defaultArg"
+                .with_field("this", "42")
+                .with_field("another", "-1")
+                .with_strict_fields(true),
+        )
+        .re_enter("Subscriptions.barSub")
+        .simple_span(&"Bar.id".with_field("self.id", "12"))
+        .simple_span(
+            &"Bar.defaultArg"
+                .with_field("this", "42")
+                .with_field("another", "-1")
+                .with_strict_fields(true),
+        )
+        .close_exited("Subscriptions.barSub");
+}
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 13c8f974a..563517dce 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -549,10 +549,14 @@ impl TraitMethod {
             }
         }
 
-        let name = if let Some(name) = meta.name.as_ref() {
-            name.as_ref().value()
+        let (name, raw_name) = if let Some(name) = meta.name.as_ref() {
+            let name = name.as_ref().value();
+            let raw_name = syn::Ident::new(name.as_str(), name.span());
+            (name, raw_name)
         } else if let syn::Pat::Ident(name) = &*argument.pat {
-            to_camel_case(&name.ident.unraw().to_string())
+            let raw_name = name.ident.clone();
+            let name = raw_name.unraw().to_string();
+            (name, raw_name)
         } else {
             ERR.custom(
                 argument.pat.span(),
@@ -577,6 +581,7 @@ impl TraitMethod {
 
         Some(MethodArgument::Regular(FieldArgument {
             name,
+            raw_name,
             ty: argument.ty.as_ref().clone(),
             description: meta.description.as_ref().map(|d| d.as_ref().value()),
             default: meta.default.as_ref().map(|v| v.as_ref().clone()),
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index a06bbd4b5..448e3990c 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -1163,6 +1163,11 @@ struct FieldArgument {
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Arguments
     name: String,
 
+    /// Raw name of this [GraphQL field argument][2] in Rust code.
+    ///
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Arguments
+    raw_name: syn::Ident,
+
     /// [Description][1] of this [GraphQL field argument][2] to put into GraphQL schema.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Descriptions
@@ -1180,6 +1185,22 @@ struct FieldArgument {
     default: Option<Option<syn::Expr>>,
 }
 
+impl FieldArgument {
+    /// Returns [`TokenStream`] with code that can be used to resolve this argument into variable.
+    #[must_use]
+    fn into_arg_resolver(&self) -> TokenStream {
+        let (raw_name, name, ty) = (&self.raw_name, &self.name, &self.ty);
+        let err_text = format!(
+            "Internal error: missing argument `{}` - validation must have failed",
+            &name,
+        );
+
+        quote! {
+            let #raw_name = args.get::<#ty>(#name).expect(#err_text);
+        }
+    }
+}
+
 /// Possible kinds of Rust trait method arguments for code generation.
 #[derive(Debug)]
 enum MethodArgument {
@@ -1259,15 +1280,8 @@ impl MethodArgument {
     fn method_resolve_field_tokens(&self) -> TokenStream {
         match self {
             Self::Regular(arg) => {
-                let (name, ty) = (&arg.name, &arg.ty);
-                let err_text = format!(
-                    "Internal error: missing argument `{}` - validation must have failed",
-                    &name,
-                );
-
-                quote! {
-                    args.get::<#ty>(#name).expect(#err_text)
-                }
+                let raw_name = &arg.raw_name;
+                quote! { #raw_name }
             }
 
             Self::Context(_) => quote! {
@@ -1393,6 +1407,11 @@ impl Field {
 
         let (name, ty, method) = (&self.name, &self.ty, &self.method);
 
+        let arg_resolvers = self
+            .arguments
+            .iter()
+            .filter_map(|arg| arg.as_regular().map(|f| f.into_arg_resolver()));
+
         let arguments = self
             .arguments
             .iter()
@@ -1405,6 +1424,8 @@ impl Field {
 
         Some(quote! {
             #name => {
+                #( #arg_resolvers )*
+
                 #tracing_span
                 #trace_sync
 
@@ -1419,6 +1440,7 @@ impl Field {
     ///
     /// [`GraphQLValueAsync::resolve_field_async`]: juniper::GraphQLValueAsync::resolve_field_async
     #[must_use]
+    #[allow(unused_variables)]
     fn method_resolve_field_async_tokens(
         &self,
         trait_ty: &syn::Type,
@@ -1426,6 +1448,11 @@ impl Field {
     ) -> TokenStream {
         let (name, ty, method) = (&self.name, &self.ty, &self.method);
 
+        let arg_resolvers = self
+            .arguments
+            .iter()
+            .filter_map(|arg| arg.as_regular().map(|f| f.into_arg_resolver()));
+
         let arguments = self
             .arguments
             .iter()
@@ -1443,6 +1470,8 @@ impl Field {
 
         quote! {
             #name => {
+                #( #arg_resolvers )*
+
                 #tracing_span
                 let fut = #fut;
                 #resolving_code
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
index 1d3c0c440..d931699bb 100644
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -50,4 +50,8 @@ impl TracedArgument for FieldArgument {
     fn name(&self) -> &str {
         self.name.as_str()
     }
+
+    fn raw_name(&self) -> &syn::Ident {
+        &self.raw_name
+    }
 }
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 705ceb791..34d632636 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -109,16 +109,25 @@ fn create(
                         );
                     }
 
-                    let resolver = quote!(
-                        let #mut_modifier #arg_ident = args
-                            .get::<#ty>(#final_name)
+                    // External argument resolver that allows to extract variables prior entering
+                    // execution context, to avoid implicit cloning. So it can be used by external
+                    // tools such as tracing that records function arguments into span.
+                    let arg_resolver = quote!(
+                        let #arg_ident: #ty = args.get::<#ty>(#final_name)
                             .unwrap_or_else(::juniper::FromInputValue::<#scalar>::from_implicit_null);
                     );
 
+                    // Used to move extracted arguments to context of execution.
+                    let resolver = quote!(
+                        let #mut_modifier #arg_ident = #arg_ident;
+                    );
+
                     let field_type = util::GraphQLTypeDefinitionFieldArg {
                         description: attrs
                             .argument(&arg_name)
                             .and_then(|arg| arg.description.as_ref().map(|d| d.value())),
+                        raw_name: arg_ident.clone(),
+                        resolver_code: arg_resolver,
                         default: attrs
                             .argument(&arg_name)
                             .and_then(|arg| arg.default.clone()),
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 133991c89..b4cfc6d82 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -687,6 +687,8 @@ impl FieldAttributes {
 #[derive(Debug)]
 pub struct GraphQLTypeDefinitionFieldArg {
     pub name: String,
+    pub raw_name: syn::Ident,
+    pub resolver_code: TokenStream,
     pub description: Option<String>,
     pub default: Option<syn::Expr>,
     pub _type: Box<syn::Type>,
@@ -854,6 +856,8 @@ impl GraphQLTypeDefinition {
                 let tracing_span = if_tracing_enabled!(tracing::span_tokens(&self, field));
                 let trace_sync = if_tracing_enabled!(tracing::sync_tokens(&self, field));
 
+                let args = field.args.iter().map(|arg| &arg.resolver_code);
+
                 let _type = if field.is_type_inferred {
                     quote!()
                 } else {
@@ -862,10 +866,12 @@ impl GraphQLTypeDefinition {
                 };
                 quote!(
                     #name => {
+                        #( #args )*
+
                         #tracing_span
                         #trace_sync
 
-                        let res #_type = (|| { #code })();
+                        let res #_type = (move || { #code })();
                         ::juniper::IntoResolvable::into(
                             res,
                             executor.context()
@@ -935,12 +941,16 @@ impl GraphQLTypeDefinition {
                     quote!(: #_type)
                 };
 
+                let args = field.args.iter().map(|arg| &arg.resolver_code);
+
                 let tracing_span = if_tracing_enabled!(tracing::span_tokens(&self, field));
                 if field.is_async {
                     let trace_async = if_tracing_enabled!(tracing::async_tokens(&self, field));
 
                     quote!(
                         #name => {
+                            #( #args )*
+
                             #tracing_span
                             let f = async move {
                                 let res #_type = async move { #code }.await;
@@ -997,10 +1007,13 @@ impl GraphQLTypeDefinition {
 
                     quote!(
                         #name => {
+                            #( #args )*
+
                             #tracing_span
+
                             let res2 = {
                                 #trace_sync
-                                let res #_type = (||{ #code })();
+                                let res #_type = (move ||{ #code })();
                                 ::juniper::IntoResolvable::into(
                                     res,
                                     executor.context()
@@ -1314,6 +1327,8 @@ impl GraphQLTypeDefinition {
                 let name = &field.name;
                 let code = &field.resolver_code;
 
+                let args = field.args.iter().map(|arg| &arg.resolver_code);
+
                 let _type;
                 if field.is_type_inferred {
                     _type = quote!();
@@ -1326,12 +1341,14 @@ impl GraphQLTypeDefinition {
                 let trace_async = if_tracing_enabled!(tracing::async_tokens(&self, field));
                 quote!(
                     #name => {
+                        #( #args )*
+
                         #trace_span
 
                         ::juniper::futures::FutureExt::boxed(async move {
                             let res #_type = async { #code }.await;
                             let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
-                            let executor= executor.as_owned_executor();
+                            let executor = executor.as_owned_executor();
                             let f = res.then(move |res| {
                                 let executor = executor.clone();
                                 let res2: ::juniper::FieldResult<_, #scalar> =
diff --git a/juniper_codegen/src/util/tracing.rs b/juniper_codegen/src/util/tracing.rs
index 0875d1650..af263ba05 100644
--- a/juniper_codegen/src/util/tracing.rs
+++ b/juniper_codegen/src/util/tracing.rs
@@ -187,6 +187,7 @@ pub trait TracedField {
 pub trait TracedArgument {
     fn ty(&self) -> &syn::Type;
     fn name(&self) -> &str;
+    fn raw_name(&self) -> &syn::Ident;
 }
 
 fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
@@ -217,12 +218,8 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
 
     let args = field.args().into_iter().filter_map(|arg| {
         let name = arg.name();
+        let raw_name = arg.raw_name();
         let arg_name = syn::LitStr::new(name, arg.ty().span());
-        let arg_getter = syn::LitStr::new(name, arg.ty().span());
-        let scalar = &ty
-            .scalar()
-            .unwrap_or_else(|| syn::parse2(quote!(::juniper::DefaultScalarValue)).unwrap());
-        let ty = arg.ty();
 
         field
             .tracing_attr()
@@ -231,11 +228,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
             .is_none()
             .then(|| {
                 quote!(
-                    #arg_name = ::juniper::tracing::field::debug(
-                        args.get::<#ty>(#arg_getter).unwrap_or_else(|| {
-                            ::juniper::FromInputValue::<#scalar>::from_implicit_null()
-                        })
-                    )
+                    #arg_name = ::juniper::tracing::field::debug(&#raw_name)
                 )
             })
     });
@@ -351,5 +344,9 @@ mod graphql_object {
         fn name(&self) -> &str {
             self.name.as_str()
         }
+
+        fn raw_name(&self) -> &syn::Ident {
+            &self.raw_name
+        }
     }
 }

From 174593ed7ad24eb5530de787b98cc7f72008c738 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 14:14:33 +0300
Subject: [PATCH 56/72] Extend example, book and small test corrections [run
 ci]

---
 docs/book/content/tracing/index.md            | 114 +++++++++---
 docs/book/tests/Cargo.toml                    |   4 +-
 examples/tracing_support/Cargo.toml           |   2 +-
 examples/tracing_support/src/main.rs          |  72 +++++++-
 juniper/Cargo.toml                            |   6 +-
 juniper/src/lib.rs                            |  10 +-
 juniper/src/tests/fixtures/mod.rs             |   2 +-
 juniper/src/tests/fixtures/tracing/mod.rs     |   2 +-
 juniper/src/tests/fixtures/tracing/schema.rs  |   9 +-
 juniper/src/tests/mod.rs                      |   2 +-
 juniper/src/tests/tracing_tests.rs            | 163 +++++++++---------
 juniper_codegen/src/graphql_interface/attr.rs |   5 +-
 12 files changed, 275 insertions(+), 116 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 55989efcd..ad6f92a24 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -2,17 +2,14 @@
 
 Juniper has optional support for the [tracing] crate for instrumentation.
 
-This feature is off by default and can be enabled via the `trace-sync`
-feature to enable tracing of a sync code, `trace-async` feature to enable
-tracing of an async code and subscriptions, and `trace` to enable 
-functionality of both `trace-sync`and `trace-async` features.
+This feature is off by default and can be enabled via the `tracing` feature.
 
 !FILENAME Cargo.toml
 
 ```toml
 [dependencies]
-juniper = { version = "0.14.7", features = ["default", "trace"]}
-tracing = "0.1.17"
+juniper = { version = "0.15.7", features = ["default", "tracing"]}
+tracing = "0.1.26"
 tracing-subscriber = "0.2.9"
 ```
 
@@ -28,10 +25,34 @@ use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variab
 #[derive(Clone, Copy, Debug)]
 struct Query;
 
+struct Foo {
+    value: i32,
+}
+
+#[graphql_object]
+impl Foo {
+    // Value resolving is pretty straightforward so we can skip tracing.
+    #[tracing(no_trace)]
+    fn value(&self) -> i32 {
+        self.value
+    }
+    
+    // Here we'll record span and it will have field with name "another" and value we passed.
+    fn multiply_value(&self, another: i32) -> i32 {
+        self.value * another
+    }
+    
+    // Here we'll record span and it will have field with name "self.value"
+    #[tracing(fields(self.value = self.value))]
+    fn square_value(&self) -> i32 {
+        self.value * self.value
+    }
+}
+
 #[graphql_object]
 impl Query {
-    async fn foo() -> i32 {
-        42
+    async fn foo() -> Foo {
+        Foo { value: 42 }
     }
 }
 
@@ -68,23 +89,58 @@ async fn main() {
 }
 ```
 
-To trace how GraphQL object being resolved you need to enable one of tracing 
-features and use `trace` argument on top-level `#[graphql_object]` and
-`#[graphql_interface]` attributes or `#[graphql]` if used with `#[derive(GraphQLObject)]`.
-`tracing` argument can be used with one of provided arguments:
-`"sync"`, `"async"`, `"skip-all"` and `"complex"`.
+## Skipping field resolvers
+
+In certain scenarios you may want to skip tracing of some fields because it
+too simple and tracing preparations of this resolver would actually take more
+time then execution in this cases you can use `#[tracing(no_trace)]` to completely
+disable tracing of this field resolver.
+
+### Example
+
+```rust
+struct User {
+    id: i32,
+}
+
+#[graphql_object(context = Context)]
+impl User {
+    #[tracing(no_trace)]
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    async fn friends(&self, context: Context) -> Vec<User> {
+        // Some async query in which you're actually interested.
+    }
+}
+```
+
+When you don't need to trace how whole object is being resolve and setting
+`#[tracing(no_trace)]` on most of the simple fields is rather inefficient you can
+use `tracing` argument on top-level `#[graphql_object]`, `#[graphql_interface]`
+or `#[graphql]` (when it used with `#[derive(GraphQLObject)]`) attributes to trace
+specific group of fields or not to trace att all. `tracing` argument can be used
+with one of the following arguments: `"sync"`, `"async"`, `"skip-all"` and `"complex"`.
  - Use `"sync"` to trace only synchronous part (struct fields and `fn`s).
  - Use `"async"` to trace only asynchronous part (`async fn`s) and
 subscriptions.
  - Use `"complex"` to trace only fields marked with `#[tracing(complex)]`
  - Use `"skip-all"` to skip tracing of all fields.
 
+**Note:** using of `trace = "sync"` with derived struct is no-op because all
+resolvers within derived GraphQL object is considered to be synchronous, also
+because of this `trace = "async"` will result in no traces.
+
 In addition you can use `#[tracing(no_trace)]` with all variants above to
 exclude field from tracing even if it belongs to traced group.
 
+**Be careful when skipping trace as it can lead to bad structured span trees,
+disabling of tracing on one level won't disable tracing in it's child methods.**
+
 If resolving of certain field requires additional arguments (when used `fn`s or
 `async fn`s) they also will be included in resulted trace (except `self` and
-`Context`). You can use `skip` argument of `#[trace]` attribute, to skip some
+`Context`). You can use `skip` argument of `#[tracing]` attribute, to skip some
 arguments, similarly to the [`skip`] for `#[instrument]`
 
 ```rust
@@ -98,31 +154,49 @@ impl Catalog {
 }
 
 struct User {
-    id: i64
+    id: i32
 }
 
 #[graphql_object]
 impl User {
-    fn id(&self) -> i64 {
+    fn id(&self) -> i32 {
         self.id
     }
 
-    async fn friends(&self) -> Vec<i64> {
+    async fn friends(&self) -> Vec<i32> {
         // async database query 
     }
 }
 ```
 
 In case above both `filter` and `count` will be recorded in [`Span`] for
-`Catalog::products(...)`.
+`Catalog::products(...)`. All fields will be recorded using their `Debug`
+implementation, if your field doesn't implement `Debug` you should skip it
+with `#[tracing(skip(<fields to skip>))]` if you still want to record it but
+for some reason you don't want or can't use `Debug` trait, consider reintroducing
+this field with `fields(field_name = some_value)`.
+
 
+### Example
+```rust
+#[derive(Clone)]
+struct NonDebug {
+    important_field: String,
+}
+
+#[tracing(skip(non_debug), fields(non_debug = non_debug.important_field))]
+fn my_query(&self, non_debug: NonDebug) -> i32 {
+    24
+}
+```
 
 ## `#[tracing]` attribute
 
-In most cases `#[tracing]` mimics behaviour of the `#[instrument]` attribute
+In most aspects `#[tracing]` mimics behaviour of the `#[instrument]` attribute
 from [tracing] crate and you could use it as a reference. With the only key
 deference you should understand, it applied implicitly to all resolvers if the
-`trace` feature is enabled.
+`tracing` feature is enabled.
 
 [tracing]: https://crates.io/crates/tracing
 [`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
+[`Span`]: https://docs.rs/tracing/0.1.26/tracing/struct.Span.html
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 687a94ed3..eef3dedbb 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Magnus Hallin <mhallin@fastmail.com>"]
 build = "build.rs"
 
 [dependencies]
-juniper = { path = "../../../juniper", features = ["default", "trace"] }
+juniper = { path = "../../../juniper", features = ["default", "tracing"] }
 juniper_iron = { path = "../../../juniper_iron" }
 juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
@@ -20,7 +20,7 @@ serde_json = "1.0"
 tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
 uuid = "0.8"
 
-tracing = "0.1.21"
+tracing = "0.1.26"
 tracing-subscriber = "0.2.12"
 
 [build-dependencies]
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index 8afe8eb4d..408536fdb 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -10,5 +10,5 @@ tracing = "0.1.21"
 tracing-subscriber = "0.2.12"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
-juniper = { path = "../../juniper", features = ["trace"] }
+juniper = { path = "../../juniper", features = ["tracing"] }
 tokio = { version = "0.2.22", features = ["rt-core", "macros", "blocking"] }
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 502c47ba5..fc2e82b41 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -4,7 +4,8 @@ extern crate tracing;
 extern crate tracing_subscriber;
 
 use juniper::{
-    graphql_object, EmptyMutation, EmptySubscription, FieldError, GraphQLEnum, RootNode, Variables,
+    graphql_object, EmptyMutation, EmptySubscription, FieldError, GraphQLEnum, GraphQLObject,
+    RootNode, Variables,
 };
 use tracing::{trace_span, Instrument as _};
 use tracing_subscriber::EnvFilter;
@@ -29,6 +30,8 @@ struct User {
 
 #[graphql_object(Context = Context)]
 impl User {
+    // `id` can be resolved pretty straight forward so we mark it with `no_trace`
+    #[tracing(no_trace)]
     fn id(&self) -> i32 {
         self.id
     }
@@ -46,6 +49,47 @@ impl User {
     }
 }
 
+#[derive(Clone, Debug)]
+struct SyncTracedUser {
+    id: i32,
+    kind: UserKind,
+    name: String,
+}
+
+// Only sync `fn`s will be traced if they're not marked with `#[tracing(no_trace)]`
+// it works similarly with `#[graphql_interface]`
+#[graphql_object(Context = Context, trace = "sync")]
+impl SyncTracedUser {
+    // Won't be traced because it's marked with `no_trace`
+    #[tracing(no_trace)]
+    fn id(&self) -> i32 {
+        self.id
+    }
+
+    // Won't be traced because it's `async fn`
+    async fn kind(&self) -> UserKind {
+        self.kind
+    }
+
+    // The only resolver that will be traced
+    fn name(&self) -> String {
+        self.name.clone()
+    }
+}
+
+#[derive(Clone, Debug, GraphQLObject)]
+#[graphql(trace = "complex")]
+struct ComplexDerivedUser {
+    // This shouldn't be traced because it's not marked with `#[tracing(complex)]`
+    id: i32,
+    // This is the only field that will be traced because it's marked with `#[tracing(complex)]`
+    #[tracing(complex)]
+    kind: UserKind,
+    // This shouldn't be traced because of `no_trace`.
+    #[tracing(complex, no_trace)]
+    name: String,
+}
+
 #[derive(Clone, Copy, Debug)]
 struct Query;
 
@@ -67,6 +111,32 @@ impl Query {
         }
     }
 
+    /// Create guest user with the given `id` and `name`.
+    #[tracing(skip(id))] // Here we skip `id` from being recorded into spans fields
+    fn guest(id: i32, name: String) -> User {
+        User {
+            id,
+            kind: UserKind::Guest,
+            name,
+        }
+    }
+
+    fn sync_user() -> SyncTracedUser {
+        SyncTracedUser {
+            id: 1,
+            kind: UserKind::User,
+            name: "Charlie".into(),
+        }
+    }
+
+    fn complex_derived() -> ComplexDerivedUser {
+        ComplexDerivedUser {
+            id: 42,
+            kind: UserKind::Admin,
+            name: "Dave".into(),
+        }
+    }
+
     /// Double the provided number.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 106cf2b93..ce99a4357 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -30,7 +30,7 @@ expose-test-schema = ["anyhow", "serde_json"]
 graphql-parser-integration = ["graphql-parser"]
 scalar-naivetime = []
 schema-language = ["graphql-parser-integration"]
-trace = ["tracing", "tracing-futures", "juniper_codegen/tracing"]
+tracing = ["tracing-rs", "tracing-futures", "juniper_codegen/tracing"]
 
 [dependencies]
 juniper_codegen = { path = "../juniper_codegen" }
@@ -52,7 +52,8 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing = { version = "0.1.23", optional = true }
+tracing-core = "0.1"
+tracing-rs = { package = "tracing", version = "0.1.23", optional = true }
 tracing-futures = { version = "0.2.5", optional = true, features = ["futures-03"] }
 
 [dev-dependencies]
@@ -60,7 +61,6 @@ bencher = "0.1.2"
 pretty_assertions = "0.7.1"
 serde_json = "1.0.2"
 tokio = { version = "1", features = ["macros", "time", "rt-multi-thread"] }
-tracing-core = { version = "0.1" }
 tracing-subscriber = "0.2"
 
 [[bench]]
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 5cb790c28..d100cf9fa 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -106,12 +106,14 @@ pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::{BoxFuture, LocalBoxFuture};
 
-// This is required by the `trace` feature.
-#[cfg(feature = "tracing")]
+// This is required by the `tracing` feature.
+//
+// Name of `tracing` crate changed to `tracing_rs` so we can use `tracing` as feature.
 #[doc(hidden)]
-pub use tracing;
-#[cfg(feature = "tracing-futures")]
+#[cfg(feature = "tracing")]
+pub extern crate tracing_rs as tracing;
 #[doc(hidden)]
+#[cfg(feature = "tracing")]
 pub use tracing_futures;
 
 // Depend on juniper_codegen and re-export everything in it.
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index 090c8c308..46de73a67 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -4,5 +4,5 @@
 pub mod starwars;
 
 /// Fixtures used to test integration with `tracing` crate.
-#[cfg(feature = "trace")]
+#[cfg(feature = "tracing")]
 pub mod tracing;
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 925062aa8..c350cd295 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -195,7 +195,7 @@ impl SubscriberAssert {
                     assert_eq!(
                         Some(&val),
                         span.fields.get(&f_name),
-                        "Field {} in span {} either doesn't exist or has value
+                        "Field {} in span {} either doesn't exist or has value \
                          different from {}, values: {:?}",
                         f_name,
                         expected.name(),
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 3c933c150..9c9f12bf1 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -1,4 +1,4 @@
-#![allow(missing_docs)]
+//! Schema that contains all necessary to test integration with `tracing` crate.
 
 use std::collections::HashMap;
 
@@ -7,12 +7,14 @@ use tracing::instrument;
 
 use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLObject};
 
+/// Test database
 #[derive(Debug)]
 pub struct Database {
     inner: HashMap<i32, String>,
 }
 
 impl Database {
+    /// Returns new [`Database`].
     pub fn new() -> Self {
         let mut inner = HashMap::new();
         inner.insert(42, "Meaning of life".to_owned());
@@ -157,6 +159,7 @@ impl Query {
     }
 }
 
+/// Test GraphQL subscriptions.
 pub struct Subscriptions;
 
 #[graphql_subscription(context = Database)]
@@ -347,11 +350,13 @@ pub trait FooBar {
         id + skipped + default + overwritten
     }
 
+    /// Field with overwritten `target` of span.
     #[tracing(target = "my_target")]
     fn target(&self) -> i32 {
         1
     }
 
+    /// Field with overwritten `level` of span.
     #[tracing(level = "warn")]
     fn level(&self) -> i32 {
         2
@@ -425,6 +430,7 @@ impl SkipAll {
     }
 }
 
+/// Derived GraphQL object marked with `trace = "skip-all"`
 #[derive(Default, GraphQLObject)]
 #[graphql(trace = "skip-all")]
 pub struct SkipAllDerived {
@@ -460,6 +466,7 @@ impl Complex {
     }
 }
 
+/// Derived GraphQL object marked with `trace = "complex"`.
 #[derive(GraphQLObject)]
 #[graphql(trace = "complex")]
 pub struct DerivedComplex {
diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index 80d3485fc..33aa0fb19 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -13,5 +13,5 @@ mod subscriptions;
 mod type_info_tests;
 
 #[cfg(test)]
-#[cfg(feature = "trace")]
+#[cfg(feature = "tracing")]
 mod tracing_tests;
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index a38724db2..289e96ae9 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -1,5 +1,5 @@
 use futures::StreamExt as _;
-use tracing::Level;
+use tracing::{subscriber::DefaultGuard, Level};
 
 use crate::{
     executor::Variables,
@@ -11,6 +11,21 @@ use crate::{
     types::scalars::EmptyMutation,
 };
 
+type TestSchema<'a> = RootNode<'a, Query, EmptyMutation<Database>, Subscriptions>;
+
+fn init_schema<'a>() -> TestSchema<'a> {
+    TestSchema::new(Query, EmptyMutation::<Database>::new(), Subscriptions)
+}
+
+fn init_tracer() -> (TestSubscriber, DefaultGuard) {
+    let subscriber = TestSubscriber::new();
+
+    (
+        subscriber.clone(),
+        tracing::subscriber::set_default(subscriber),
+    )
+}
+
 #[test]
 fn test_execute_sync_clean() {
     let doc = r#"
@@ -21,15 +36,11 @@ fn test_execute_sync_clean() {
                 skipArgument(name: "name?", meaningOfLife: 42)
             }
         }"#;
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
+    let (handle, _guard) = init_tracer();
 
-    let collector = TestSubscriber::new();
-    let handle = collector.clone();
-
-    tracing::subscriber::with_default(collector, || {
-        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).ok();
-    });
+    juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).ok();
 
     handle
         .assert()
@@ -57,15 +68,11 @@ fn test_execute_sync_with_error() {
                 name
             }
         }"#;
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
+    let (handle, _guard) = init_tracer();
 
-    tracing::subscriber::with_default(subscriber, || {
-        juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).err();
-    });
+    juniper::execute_sync(doc, None, &schema, &Variables::new(), &database).err();
 
     handle
         .assert()
@@ -77,6 +84,42 @@ fn test_execute_sync_with_error() {
         .close_exited("execute_sync");
 }
 
+#[tokio::test]
+async fn records_sub_spans() {
+    let doc = r#"
+        {
+            bar(id: 15) {
+                tracedData
+                nonTracedData
+            }
+        }
+        "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (handle, _guard) = init_tracer();
+
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("rule_validation")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.bar")
+        // To check whether context hasn't leaked.
+        .enter_new_span(&"Bar.tracedData".with_strict_fields(true))
+        // `traced_query` is fn marked with `#[instrument]` from `tracing` crate
+        .simple_span(&"traced_query".with_field("id", "15"))
+        .close_exited("Bar.tracedData")
+        // Should has no child spans because `non_traced_query` doesn't marked with `#[instrument]`
+        .simple_span(&"Bar.nonTracedData".with_strict_fields(true))
+        .close_exited("Query.bar")
+        .close_exited("execute_validated_query")
+        .close_exited("execute");
+}
+
 #[tokio::test]
 async fn test_no_trace_field() {
     let doc = r#"
@@ -96,17 +139,12 @@ async fn test_no_trace_field() {
                 }
             }
         }"#;
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
+    let (handle, _guard) = init_tracer();
 
-    let _guard = tracing::subscriber::set_default(subscriber);
-
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -140,17 +178,12 @@ async fn test_impl_fn_args() {
                 defaultArg(another: -1)
             }
         }"#;
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
+    let (handle, _guard) = init_tracer();
 
-    let _guard = tracing::subscriber::set_default(subscriber);
-
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -193,17 +226,12 @@ async fn test_custom_fields() {
             }
         }
     "#;
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
-
-    let _guard = tracing::subscriber::set_default(subscriber);
+    let (handle, _guard) = init_tracer();
 
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -250,18 +278,12 @@ async fn overwrite_level_and_target() {
             }
         }
         "#;
-
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
+    let (handle, _guard) = init_tracer();
 
-    let _guard = tracing::subscriber::set_default(subscriber);
-
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -359,18 +381,12 @@ async fn graphql_object_trace_arg() {
             }
         }
         "#;
-
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
+    let (handle, _guard) = init_tracer();
 
-    let _guard = tracing::subscriber::set_default(subscriber);
-
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -438,18 +454,12 @@ async fn graphql_interface_trace_arg() {
             }
         }
         "#;
-
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
+    let (handle, _guard) = init_tracer();
 
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
-
-    let _guard = tracing::subscriber::set_default(subscriber);
-
-    juniper::execute(doc, None, &schema, &Variables::new(), &database)
-        .await
-        .err();
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
 
     handle
         .assert()
@@ -485,14 +495,9 @@ async fn subscription_tracing() {
             }
         }
         "#;
-
+    let schema = init_schema();
     let database = Database::new();
-    let schema = RootNode::new(Query, EmptyMutation::<Database>::new(), Subscriptions);
-
-    let subscriber = TestSubscriber::new();
-    let handle = subscriber.clone();
-
-    let _guard = tracing::subscriber::set_default(subscriber);
+    let (handle, _guard) = init_tracer();
 
     let request = crate::http::GraphQLRequest::new(doc.to_owned(), None, None);
 
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 563517dce..3c2b4867c 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -6,8 +6,6 @@ use proc_macro2::{Span, TokenStream};
 use quote::{quote, ToTokens as _};
 use syn::{ext::IdentExt as _, parse_quote, spanned::Spanned};
 
-#[cfg(feature = "tracing")]
-use crate::util::tracing;
 use crate::{
     common::{
         parse::{self, TypeExt as _},
@@ -17,6 +15,9 @@ use crate::{
     util::{path_eq_single, span_container::SpanContainer, to_camel_case},
 };
 
+#[cfg(feature = "tracing")]
+use crate::util::tracing;
+
 use super::{
     inject_async_trait, ArgumentMeta, Definition, EnumType, Field, FieldArgument, ImplMeta,
     Implementer, ImplementerDowncast, MethodArgument, MethodMeta, TraitMeta, TraitObjectType, Type,

From 2fe41062eaff878ea66c6dfe1869598d96c9154b Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 15:04:52 +0300
Subject: [PATCH 57/72] Fix book imports [run ci]

---
 docs/book/content/tracing/index.md   | 32 +++++++++++++++++-----------
 docs/book/tests/Cargo.toml           |  5 ++---
 examples/tracing_support/Cargo.toml  |  4 ++--
 examples/tracing_support/src/main.rs | 27 ++++++++++++++++++++++-
 4 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index ad6f92a24..9768a334b 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -10,7 +10,7 @@ This feature is off by default and can be enabled via the `tracing` feature.
 [dependencies]
 juniper = { version = "0.15.7", features = ["default", "tracing"]}
 tracing = "0.1.26"
-tracing-subscriber = "0.2.9"
+tracing-subscriber = "0.2.15"
 ```
 
 ## Usage
@@ -82,7 +82,14 @@ async fn main() {
 
     // When run with `RUST_LOG=trace cargo run`, this should output traces /
     // span events to `stdout`.
-    let query = "{ foo }";
+    let query = r#"
+        {
+            foo { 
+                value
+                multiplyValues(another: 23)
+                squareValue
+            }
+        }"#;
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &())
         .await
         .unwrap();
@@ -91,10 +98,10 @@ async fn main() {
 
 ## Skipping field resolvers
 
-In certain scenarios you may want to skip tracing of some fields because it
-too simple and tracing preparations of this resolver would actually take more
-time then execution in this cases you can use `#[tracing(no_trace)]` to completely
-disable tracing of this field resolver.
+In certain scenarios you may want to skip tracing of some fields because it too
+simple and straightforward that tracing preparations of this resolver would actually
+take more time then execution. In this cases you can use `#[tracing(no_trace)]` to
+completely disable tracing of this field resolver.
 
 ### Example
 
@@ -116,12 +123,13 @@ impl User {
 }
 ```
 
-When you don't need to trace how whole object is being resolve and setting
-`#[tracing(no_trace)]` on most of the simple fields is rather inefficient you can
-use `tracing` argument on top-level `#[graphql_object]`, `#[graphql_interface]`
-or `#[graphql]` (when it used with `#[derive(GraphQLObject)]`) attributes to trace
-specific group of fields or not to trace att all. `tracing` argument can be used
-with one of the following arguments: `"sync"`, `"async"`, `"skip-all"` and `"complex"`.
+Manually setting `#[tracing(no_traces)]` to avoid tracing of all, let's say for
+example synchronous field resolvers is rather inefficient when you have GraphQL
+object with too much fields. In this case you can use `tracing` argument on
+top-level `#[graphql_object]`, `#[graphql_interface]` or `#[graphql]` (when it
+used with `#[derive(GraphQLObject)]`) attributes to trace specific group of
+fields or not to trace at all. `tracing` argument can be used with one of the
+following arguments: `"sync"`, `"async"`, `"complex"` or `"skip-all"`.
  - Use `"sync"` to trace only synchronous part (struct fields and `fn`s).
  - Use `"async"` to trace only asynchronous part (`async fn`s) and
 subscriptions.
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index eef3dedbb..97961b296 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -12,16 +12,15 @@ juniper_subscriptions = { path = "../../../juniper_subscriptions" }
 
 derive_more = "0.99"
 futures = "0.3"
-tokio = { version = "0.2", features = ["rt-core", "blocking", "stream", "rt-util", "macros"] }
 iron = "0.5"
 mount = "0.4"
 skeptic = "0.13"
 serde_json = "1.0"
-tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
+tokio = { version = "1", features = ["macros", "rt-core", "blocking", "stream", "rt-util", "rt-multi-thread"] }
 uuid = "0.8"
 
 tracing = "0.1.26"
-tracing-subscriber = "0.2.12"
+tracing-subscriber = "0.2.15"
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/examples/tracing_support/Cargo.toml b/examples/tracing_support/Cargo.toml
index 408536fdb..551d742f8 100644
--- a/examples/tracing_support/Cargo.toml
+++ b/examples/tracing_support/Cargo.toml
@@ -6,8 +6,8 @@ edition = "2018"
 publish = false
 
 [dependencies]
-tracing = "0.1.21"
-tracing-subscriber = "0.2.12"
+tracing = "0.1.26"
+tracing-subscriber = "0.2.15"
 # Note instead of a path you would use the proper Juniper version in your own
 # project.
 juniper = { path = "../../juniper", features = ["tracing"] }
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index fc2e82b41..5908ba63e 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -30,7 +30,7 @@ struct User {
 
 #[graphql_object(Context = Context)]
 impl User {
-    // `id` can be resolved pretty straight forward so we mark it with `no_trace`
+    // `id` can be resolved pretty straightforward so we mark it with `no_trace`
     #[tracing(no_trace)]
     fn id(&self) -> i32 {
         self.id
@@ -188,4 +188,29 @@ async fn main() {
     // This should output a validation error in the middle of other spans.
     let query = "{ bob { field_that_does_not_exist } }";
     let _ = juniper::execute_sync(query, None, &root, &vars, &ctx);
+
+    // If you use tracing with something like `jaeger_opentracing` span with name
+    // 'Query.guest' which has field 'name' with value '"Not Bob"' and 1 child span
+    // 'User.kind'. There won't be traces to 'User.id' because we marked it with
+    // `#[tracing(no_trace)]`
+    let query = "{ guest(id: 1, name: \"Not Bob\") { id  kind} }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .await
+        .unwrap();
+
+    // Here you'll see span 'Query.syncUser' with one child span
+    // 'SyncTracedUser.kind' because it's synchronous and not marked with
+    // `#[tracing(no_trace)]`.
+    let query = "{ syncUser { id name kind} }";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .await
+        .unwrap();
+
+    // Here you'll see span 'Query.complexUser' with one child span
+    // 'ComplexDerivedUser.kind' because it's marked with `#[tracing(complex)]`
+    // and not marked with `#[tracing(no_trace)]`.
+    let query = "{ complexUser { id name kind }}";
+    let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
+        .await
+        .unwrap();
 }

From 7b9ac11d049e93981dc049a04ca5c820c69b07cb Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 15:10:42 +0300
Subject: [PATCH 58/72] Fix tokio dependency in book [run ci]

---
 docs/book/tests/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index 97961b296..a5791b6b2 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -16,7 +16,7 @@ iron = "0.5"
 mount = "0.4"
 skeptic = "0.13"
 serde_json = "1.0"
-tokio = { version = "1", features = ["macros", "rt-core", "blocking", "stream", "rt-util", "rt-multi-thread"] }
+tokio = { version = "1", features = ["macros", "rt-core", "stream", "rt-util", "rt-multi-thread"] }
 uuid = "0.8"
 
 tracing = "0.1.26"

From 2772b32a9f20adf1ddae724f08a50f63f0394de3 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 15:48:57 +0300
Subject: [PATCH 59/72] Fix book build [run ci]

---
 .../advanced/implicit_and_explicit_null.md    |  2 +-
 docs/book/content/quickstart.md               |  4 +-
 docs/book/content/tracing/index.md            | 45 +++++++++++++++++--
 docs/book/content/types/input_objects.md      |  4 +-
 docs/book/tests/Cargo.toml                    |  4 +-
 juniper_codegen/src/util/tracing.rs           |  2 +-
 6 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/docs/book/content/advanced/implicit_and_explicit_null.md b/docs/book/content/advanced/implicit_and_explicit_null.md
index 014249a19..ecdab8daa 100644
--- a/docs/book/content/advanced/implicit_and_explicit_null.md
+++ b/docs/book/content/advanced/implicit_and_explicit_null.md
@@ -69,7 +69,7 @@ In Juniper, this can be done using the `Nullable` type:
 # extern crate juniper;
 use juniper::{FieldResult, Nullable};
 
-#[derive(juniper::GraphQLInputObject)]
+#[derive(Debug, juniper::GraphQLInputObject)]
 struct UserPatchInput {
     pub favorite_number: Nullable<i32>,
     pub least_favorite_number: Nullable<i32>,
diff --git a/docs/book/content/quickstart.md b/docs/book/content/quickstart.md
index 7af0bb02e..a119ef4a7 100644
--- a/docs/book/content/quickstart.md
+++ b/docs/book/content/quickstart.md
@@ -41,7 +41,7 @@ use juniper::{
 #     fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
 # }
 
-#[derive(GraphQLEnum)]
+#[derive(Debug, GraphQLEnum)]
 enum Episode {
     NewHope,
     Empire,
@@ -59,7 +59,7 @@ struct Human {
 
 // There is also a custom derive for mapping GraphQL input objects.
 
-#[derive(GraphQLInputObject)]
+#[derive(Debug, GraphQLInputObject)]
 #[graphql(description = "A humanoid creature in the Star Wars universe")]
 struct NewHuman {
     name: String,
diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 9768a334b..0eb0b07a6 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -38,7 +38,7 @@ impl Foo {
     }
     
     // Here we'll record span and it will have field with name "another" and value we passed.
-    fn multiply_value(&self, another: i32) -> i32 {
+    fn multiply_values(&self, another: i32) -> i32 {
         self.value * another
     }
     
@@ -106,6 +106,13 @@ completely disable tracing of this field resolver.
 ### Example
 
 ```rust
+# extern crate juniper;
+# use juniper::graphql_object;
+# fn main() {}
+#
+# struct Context;
+# impl juniper::Context for Context {}
+
 struct User {
     id: i32,
 }
@@ -117,8 +124,9 @@ impl User {
         self.id
     }
 
-    async fn friends(&self, context: Context) -> Vec<User> {
+    async fn friends(&self, context: &Context) -> Vec<User> {
         // Some async query in which you're actually interested.
+        vec![]
     }
 }
 ```
@@ -152,12 +160,28 @@ If resolving of certain field requires additional arguments (when used `fn`s or
 arguments, similarly to the [`skip`] for `#[instrument]`
 
 ```rust
+# extern crate juniper;
+# use juniper::{graphql_object, GraphQLObject};
+#
+# fn main() {}
+#
+# struct Context;
+# impl juniper::Context for Context {}
+#
+# type Filter = i32;
+#
+# #[derive(GraphQLObject)]
+# struct Product {
+#     id: i32   
+# }
+
 struct Catalog;
 
 #[graphql_object]
 impl Catalog {
     async fn products(filter: Filter, count: i32) -> Vec<Product> {
         // Some query
+# unimplemented!()
     }
 }
 
@@ -173,6 +197,7 @@ impl User {
 
     async fn friends(&self) -> Vec<i32> {
         // async database query 
+# unimplemented!()
     }
 }
 ```
@@ -187,15 +212,27 @@ this field with `fields(field_name = some_value)`.
 
 ### Example
 ```rust
-#[derive(Clone)]
+# extern crate juniper;
+# use juniper::graphql_object;
+#
+# fn main() {}
+#
+# struct Context;
+# impl juniper::Context for Context {}
+# struct Query;
+
+#[derive(Clone, juniper::GraphQLInputObject)]
 struct NonDebug {
     important_field: String,
 }
 
-#[tracing(skip(non_debug), fields(non_debug = non_debug.important_field))]
+# #[graphql_object]
+# impl Query {
+#[tracing(skip(non_debug), fields(non_debug = non_debug.important_field.clone()))]
 fn my_query(&self, non_debug: NonDebug) -> i32 {
     24
 }
+# }
 ```
 
 ## `#[tracing]` attribute
diff --git a/docs/book/content/types/input_objects.md b/docs/book/content/types/input_objects.md
index ef103aacf..ebfe4a094 100644
--- a/docs/book/content/types/input_objects.md
+++ b/docs/book/content/types/input_objects.md
@@ -7,7 +7,7 @@ attribute, similar to simple objects and enums:
 ```rust
 # #![allow(unused_variables)]
 # extern crate juniper;
-#[derive(juniper::GraphQLInputObject)]
+#[derive(Debug, juniper::GraphQLInputObject)]
 struct Coordinate {
     latitude: f64,
     longitude: f64
@@ -36,7 +36,7 @@ and add documentation to both the type and the fields:
 ```rust
 # #![allow(unused_variables)]
 # extern crate juniper;
-#[derive(juniper::GraphQLInputObject)]
+#[derive(Debug, juniper::GraphQLInputObject)]
 #[graphql(name="Coordinate", description="A position on the globe")]
 struct WorldCoordinate {
     #[graphql(name="lat", description="The latitude")]
diff --git a/docs/book/tests/Cargo.toml b/docs/book/tests/Cargo.toml
index a5791b6b2..5bb2350a3 100644
--- a/docs/book/tests/Cargo.toml
+++ b/docs/book/tests/Cargo.toml
@@ -16,11 +16,11 @@ iron = "0.5"
 mount = "0.4"
 skeptic = "0.13"
 serde_json = "1.0"
-tokio = { version = "1", features = ["macros", "rt-core", "stream", "rt-util", "rt-multi-thread"] }
+tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }
 uuid = "0.8"
 
 tracing = "0.1.26"
-tracing-subscriber = "0.2.15"
+tracing-subscriber = "0.2.1"
 
 [build-dependencies]
 skeptic = "0.13"
diff --git a/juniper_codegen/src/util/tracing.rs b/juniper_codegen/src/util/tracing.rs
index af263ba05..212969085 100644
--- a/juniper_codegen/src/util/tracing.rs
+++ b/juniper_codegen/src/util/tracing.rs
@@ -223,7 +223,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
 
         field
             .tracing_attr()
-            .map(|t| t.skip.get(name))
+            .map(|t| t.skip.get(&raw_name.to_string()))
             .flatten()
             .is_none()
             .then(|| {

From b2a285a5a21a176b272b88d17a116e327482fe69 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 23 Jul 2021 16:21:13 +0300
Subject: [PATCH 60/72] Minor corrections [run ci]

---
 juniper/Cargo.toml                | 4 ++--
 juniper/src/tests/fixtures/mod.rs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index ce99a4357..917d5aa6d 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -33,7 +33,7 @@ schema-language = ["graphql-parser-integration"]
 tracing = ["tracing-rs", "tracing-futures", "juniper_codegen/tracing"]
 
 [dependencies]
-juniper_codegen = { path = "../juniper_codegen" }
+juniper_codegen = { version = "0.15.7", path = "../juniper_codegen"  }
 
 anyhow = { version = "1.0.32", optional = true, default-features = false }
 async-trait = "0.1.39"
@@ -52,7 +52,6 @@ static_assertions = "1.1"
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing-core = "0.1"
 tracing-rs = { package = "tracing", version = "0.1.23", optional = true }
 tracing-futures = { version = "0.2.5", optional = true, features = ["futures-03"] }
 
@@ -61,6 +60,7 @@ bencher = "0.1.2"
 pretty_assertions = "0.7.1"
 serde_json = "1.0.2"
 tokio = { version = "1", features = ["macros", "time", "rt-multi-thread"] }
+tracing-core = "0.1"
 tracing-subscriber = "0.2"
 
 [[bench]]
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index 46de73a67..a5217397e 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -4,5 +4,5 @@
 pub mod starwars;
 
 /// Fixtures used to test integration with `tracing` crate.
-#[cfg(feature = "tracing")]
+#[cfg(all(test, feature = "tracing"))]
 pub mod tracing;

From 33d69529c1b15d26794e6301b7be0d6304aba5e0 Mon Sep 17 00:00:00 2001
From: tyranron <tyranron@gmail.com>
Date: Fri, 23 Jul 2021 18:59:00 +0300
Subject: [PATCH 61/72] Some corrections

---
 juniper/Cargo.toml                           |   7 +-
 juniper/src/lib.rs                           |  44 ++++----
 juniper/src/macros/mod.rs                    |   1 -
 juniper/src/macros/tracing.rs                |  10 +-
 juniper/src/tests/fixtures/mod.rs            |   1 -
 juniper/src/tests/fixtures/tracing/mod.rs    |  10 +-
 juniper/src/tests/fixtures/tracing/schema.rs | 101 ++++++++++---------
 juniper/src/tests/mod.rs                     |   6 +-
 juniper/src/tests/tracing_tests.rs           |   2 +-
 juniper/src/util.rs                          |  11 +-
 10 files changed, 97 insertions(+), 96 deletions(-)

diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml
index 917d5aa6d..c764cf241 100644
--- a/juniper/Cargo.toml
+++ b/juniper/Cargo.toml
@@ -30,7 +30,7 @@ expose-test-schema = ["anyhow", "serde_json"]
 graphql-parser-integration = ["graphql-parser"]
 scalar-naivetime = []
 schema-language = ["graphql-parser-integration"]
-tracing = ["tracing-rs", "tracing-futures", "juniper_codegen/tracing"]
+tracing = ["tracing-01", "tracing-futures", "juniper_codegen/tracing"]
 
 [dependencies]
 juniper_codegen = { version = "0.15.7", path = "../juniper_codegen"  }
@@ -49,12 +49,11 @@ serde = { version = "1.0.8", features = ["derive"], default-features = false }
 serde_json = { version = "1.0.2", default-features = false, optional = true }
 smartstring = "0.2.6"
 static_assertions = "1.1"
+tracing-01 = { package = "tracing", version = "0.1.23", optional = true }
+tracing-futures = { version = "0.2.5", features = ["futures-03"], optional = true }
 url = { version = "2.0", optional = true }
 uuid = { version = "0.8", default-features = false, optional = true }
 
-tracing-rs = { package = "tracing", version = "0.1.23", optional = true }
-tracing-futures = { version = "0.2.5", optional = true, features = ["futures-03"] }
-
 [dev-dependencies]
 bencher = "0.1.2"
 pretty_assertions = "0.7.1"
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index d100cf9fa..385a3e352 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -106,12 +106,11 @@ pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
 #[doc(inline)]
 pub use futures::future::{BoxFuture, LocalBoxFuture};
 
-// This is required by the `tracing` feature.
-//
-// Name of `tracing` crate changed to `tracing_rs` so we can use `tracing` as feature.
+// These are required by the code generated via the `juniper_codegen` macros
+// when the `tracing` feature is enabled.
 #[doc(hidden)]
 #[cfg(feature = "tracing")]
-pub extern crate tracing_rs as tracing;
+pub use tracing_01 as tracing;
 #[doc(hidden)]
 #[cfg(feature = "tracing")]
 pub use tracing_futures;
@@ -151,7 +150,7 @@ mod executor_tests;
 
 // Needs to be public because macros use it.
 pub use crate::util::to_camel_case;
-#[cfg(feature = "tracing-futures")]
+#[cfg(feature = "tracing")]
 pub use crate::util::tracing::InstrumentInternal;
 
 use crate::{
@@ -214,13 +213,12 @@ impl<'a> fmt::Display for GraphQLError<'a> {
         match self {
             GraphQLError::ParseError(error) => write!(f, "{}", error),
             GraphQLError::ValidationError(errors) => {
-                let errors = errors
-                    .iter()
-                    .map(|e| format!("{}", e))
-                    .collect::<Vec<_>>()
-                    .join("\n");
-                write!(f, "{}", errors)?;
-                Ok(())
+                if let Some((last, other)) = errors.split_last() {
+                    for err in other {
+                        writeln!(f, "{}", err)?;
+                    }
+                    write!(f, "{}", last)?;
+                }
             }
             GraphQLError::NoOperationProvided => write!(f, "No operation provided"),
             GraphQLError::MultipleOperationsProvided => write!(f, "Multiple operations provided"),
@@ -252,7 +250,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
-        __juniper_span_trace!("rule_validation");
+        __juniper_span_trace!("validate_document");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -260,7 +258,6 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {}", gql_error);
-
             return Err(gql_error);
         }
     }
@@ -274,7 +271,6 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {}", gql_error);
-
             return Err(gql_error);
         }
     }
@@ -306,7 +302,7 @@ where
     let document = parse_document_source(document_source, &root_node.schema)?;
 
     {
-        __juniper_span_trace!("rule_validation");
+        __juniper_span_trace!("validate_document");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -314,7 +310,6 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {}", gql_error);
-
             return Err(gql_error);
         }
     }
@@ -328,16 +323,14 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {}", gql_error);
-
             return Err(gql_error);
         }
     }
 
-    let f = executor::execute_validated_query_async(
+    let fut = executor::execute_validated_query_async(
         &document, operation, root_node, variables, context,
     );
-
-    __juniper_instrument_trace!(f, "execute_validated_query").await
+    __juniper_instrument_trace!(fut, "execute_validated_query_async").await
 }
 
 /// Resolve subscription into `ValuesStream`
@@ -364,7 +357,7 @@ where
         parse_document_source(document_source, &root_node.schema)?;
 
     {
-        __juniper_span_trace!("rule_validation");
+        __juniper_span_trace!("validate_document");
         let mut ctx = ValidatorContext::new(&root_node.schema, &document);
         visit_all_rules(&mut ctx, &document);
 
@@ -372,7 +365,6 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {:?}", gql_error);
-
             return Err(gql_error);
         }
     }
@@ -386,16 +378,14 @@ where
         if !errors.is_empty() {
             let gql_error = GraphQLError::ValidationError(errors);
             __juniper_trace!("validation_error: {:?}", gql_error);
-
             return Err(gql_error);
         }
     }
 
-    let f = executor::resolve_validated_subscription(
+    let fut = executor::resolve_validated_subscription(
         &document, operation, root_node, variables, context,
     );
-
-    __juniper_instrument_trace!(f, "resolve_validated_subscription").await
+    __juniper_instrument_trace!(fut, "resolve_validated_subscription").await
 }
 
 /// Execute the reference introspection query in the provided schema
diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index ff6dd3d8a..4ddd9e591 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -4,5 +4,4 @@ pub mod helper;
 
 #[cfg(test)]
 mod tests;
-
 mod tracing;
diff --git a/juniper/src/macros/tracing.rs b/juniper/src/macros/tracing.rs
index 05882e398..ba372598e 100644
--- a/juniper/src/macros/tracing.rs
+++ b/juniper/src/macros/tracing.rs
@@ -1,3 +1,7 @@
+//! Macros to instrument [`tracing`] spans inside this crate.
+//!
+//! [`tracing`]: crate::tracing
+
 // Macros to instrument future spans.
 
 #[doc(hidden)]
@@ -23,16 +27,16 @@ macro_rules! __juniper_instrument_trace {
     }}
 }
 
-// Macros to instrument (non-future) spans.
+// Macros to instrument non-future spans.
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! __juniper_span_internal {
     ($trace_type:ident; $($element:expr),*) => {
         #[cfg(feature = "tracing")]
-        let myspan = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
+        let __span = $crate::tracing::span!($crate::tracing::Level::$trace_type, ($($element),*));
         #[cfg(feature = "tracing")]
-        let _enter = myspan.enter();
+        let _span_enter = __span.enter();
     };
 }
 
diff --git a/juniper/src/tests/fixtures/mod.rs b/juniper/src/tests/fixtures/mod.rs
index a5217397e..d048d197a 100644
--- a/juniper/src/tests/fixtures/mod.rs
+++ b/juniper/src/tests/fixtures/mod.rs
@@ -3,6 +3,5 @@
 /// GraphQL schema and data from Star Wars.
 pub mod starwars;
 
-/// Fixtures used to test integration with `tracing` crate.
 #[cfg(all(test, feature = "tracing"))]
 pub mod tracing;
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index c350cd295..10a433cb8 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -1,15 +1,19 @@
-//! Fixtures used to test integration with `tracing` crate.
+//! Fixtures used to test integration with [`tracing`] crate.
+//!
+//! [`tracing`]: crate::tracing
 
 pub mod schema;
 
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
-use tracing::{
+use tracing_core::{span, Subscriber};
+
+use crate::tracing::{
+    self,
     field::{Field, Visit},
     span::{Attributes, Record},
     Event, Level, Metadata,
 };
-use tracing_core::{span, Subscriber};
 
 /// Information about `tracing` span recorded within tests.
 #[derive(Clone, Debug)]
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 9c9f12bf1..91728db94 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -1,28 +1,32 @@
-//! Schema that contains all necessary to test integration with `tracing` crate.
+//! Schema that contains all the necessities to test integration with
+//! [`tracing`] crate.
 
 use std::collections::HashMap;
 
 use futures::stream::{self, BoxStream};
-use tracing::instrument;
 
-use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLObject};
+use crate::{
+    graphql_interface, graphql_object, graphql_subscription, tracing, Context, GraphQLObject,
+};
 
-/// Test database
+/// Test database.
 #[derive(Debug)]
 pub struct Database {
     inner: HashMap<i32, String>,
 }
 
+impl Context for Database {}
+
 impl Database {
-    /// Returns new [`Database`].
+    /// Returns a new [`Database`].
     pub fn new() -> Self {
         let mut inner = HashMap::new();
         inner.insert(42, "Meaning of life".to_owned());
         Self { inner }
     }
 
-    /// Query mock, instrumented by `tracing` crate.
-    #[instrument(skip(self))]
+    /// Query mock, instrumented by [`tracing`] crate.
+    #[tracing::instrument(skip(self))]
     pub fn traced_query(&self, id: i32) -> Option<String> {
         self.inner.get(&id).cloned()
     }
@@ -33,9 +37,7 @@ impl Database {
     }
 }
 
-impl Context for Database {}
-
-/// Query root with various queries used to test tracing compatibility.
+/// Query root with various queries used to test [`tracing`] compatibility.
 pub struct Query;
 
 #[graphql_object(context = Database)]
@@ -45,7 +47,7 @@ impl Query {
         Foo { id: 37 }
     }
 
-    /// Sync query with argument.
+    /// Sync query with an argument.
     fn bar(id: i32) -> Bar {
         Bar { id }
     }
@@ -60,12 +62,12 @@ impl Query {
         }
     }
 
-    /// Async query with argument.
+    /// Async query with an argument.
     async fn async_bar(id: i32) -> Bar {
         Bar { id }
     }
 
-    /// Query that returns `Foo` wrapped in `FooBar`.
+    /// Query that returns an object wrapped into GraphQL interface.
     fn foo_bar() -> FooBarValue {
         FooBarValue::Foo(Foo { id: 1 })
     }
@@ -159,7 +161,7 @@ impl Query {
     }
 }
 
-/// Test GraphQL subscriptions.
+/// Subscriptions root with various queries used to test [`tracing`] compatibility.
 pub struct Subscriptions;
 
 #[graphql_subscription(context = Database)]
@@ -171,6 +173,21 @@ impl Subscriptions {
     }
 }
 
+macro_rules! build_impl {
+    ($ty:ident, $trt:ident) => {
+        #[graphql_interface(async)]
+        impl $trt for $ty {
+            fn sync_fn(&self) -> i32 {
+                1
+            }
+
+            async fn async_fn(&self) -> i32 {
+                2
+            }
+        }
+    };
+}
+
 /// Simple GraphQL object.
 pub struct Foo {
     id: i32,
@@ -202,13 +219,13 @@ impl Foo {
         meaning_of_life
     }
 
-    /// Field with it's `target` overwritten.
+    /// Field with its `target` being overwritten.
     #[tracing(target = "my_target")]
     fn target(&self) -> bool {
         true
     }
 
-    /// Field with it's `level` overwritten.
+    /// Field with its `level` being overwritten.
     #[tracing(level = "warn")]
     fn level(&self) -> bool {
         true
@@ -239,7 +256,7 @@ impl Bar {
         self.id
     }
 
-    /// Field that has signature identical to `FooBar`s one but in fact traced.
+    /// Field having signature identical to `FooBar`'s one, but being traced in fact.
     fn non_traced(&self) -> bool {
         false
     }
@@ -256,7 +273,7 @@ impl Bar {
         context.traced_query(self.id)
     }
 
-    /// Non traced database query.
+    /// Non-traced database query.
     async fn non_traced_data(&self, context: &Database) -> Option<String> {
         context.non_traced_query(self.id)
     }
@@ -273,23 +290,23 @@ impl FooBar for Bar {
     }
 }
 
-/// Derived `GraphQLObject`.
+/// Derived [`GraphQLObject`].
 #[derive(GraphQLObject)]
 #[graphql(impl = FooBarValue, context = Database)]
 pub struct DerivedFoo {
-    /// Resolver from that has context bound and const bound trace fields.
+    /// Resolver having context bound and const bound trace fields.
     #[tracing(fields(self.id = self.id, custom_fields = "work"))]
     id: i32,
 
-    /// Field marked with `no_trace` within derived GraphQLObject.
+    /// Field marked with `no_trace` within derived [`GraphQLObject`].
     #[tracing(no_trace)]
     non_traced: String,
 
-    /// Field with it's `target` overwritten.
+    /// Field with its `target` being overwritten.
     #[tracing(target = "my_target")]
     target: bool,
 
-    /// Field with it's `level` overwritten.
+    /// Field with its `level` being overwritten.
     #[tracing(level = "warn")]
     level: bool,
 }
@@ -363,7 +380,7 @@ pub trait FooBar {
     }
 }
 
-/// GraphQL object marked with `trace = "skip-sync"`
+/// GraphQL object marked with `trace = "skip-sync"`.
 pub struct TraceSync;
 
 #[graphql_object(
@@ -380,6 +397,9 @@ impl TraceSync {
     }
 }
 
+build_impl!(TraceSync, InterfacedSimple);
+build_impl!(TraceSync, InterfacedSync);
+
 /// Derived GraphQL object marked with `trace = "sync"`.
 #[derive(Default, GraphQLObject)]
 #[graphql(trace = "sync")]
@@ -405,7 +425,9 @@ impl TraceAsync {
     }
 }
 
-/// Derived GraphQL object
+build_impl!(TraceAsync, InterfacedAsync);
+
+/// Derived GraphQL object.
 #[derive(Default, GraphQLObject)]
 #[graphql(trace = "async")]
 pub struct AsyncDerived {
@@ -430,7 +452,9 @@ impl SkipAll {
     }
 }
 
-/// Derived GraphQL object marked with `trace = "skip-all"`
+build_impl!(SkipAll, InterfacedSkipAll);
+
+/// Derived GraphQL object marked with `trace = "skip-all"`.
 #[derive(Default, GraphQLObject)]
 #[graphql(trace = "skip-all")]
 pub struct SkipAllDerived {
@@ -438,7 +462,7 @@ pub struct SkipAllDerived {
     sync: i32,
 }
 
-/// GraphQL object marked with `trace = "complex"`
+/// GraphQL object marked with `trace = "complex"`.
 pub struct Complex;
 
 #[graphql_object(
@@ -466,6 +490,8 @@ impl Complex {
     }
 }
 
+build_impl!(Complex, InterfacedComplex);
+
 /// Derived GraphQL object marked with `trace = "complex"`.
 #[derive(GraphQLObject)]
 #[graphql(trace = "complex")]
@@ -528,24 +554,3 @@ trait InterfacedComplex {
     #[tracing(complex)]
     async fn async_fn(&self) -> i32;
 }
-
-macro_rules! build_impl {
-    ($ty:ident, $trt:ident) => {
-        #[graphql_interface(async)]
-        impl $trt for $ty {
-            fn sync_fn(&self) -> i32 {
-                1
-            }
-
-            async fn async_fn(&self) -> i32 {
-                2
-            }
-        }
-    };
-}
-
-build_impl!(TraceSync, InterfacedSimple);
-build_impl!(TraceSync, InterfacedSync);
-build_impl!(TraceAsync, InterfacedAsync);
-build_impl!(SkipAll, InterfacedSkipAll);
-build_impl!(Complex, InterfacedComplex);
diff --git a/juniper/src/tests/mod.rs b/juniper/src/tests/mod.rs
index 33aa0fb19..079ada020 100644
--- a/juniper/src/tests/mod.rs
+++ b/juniper/src/tests/mod.rs
@@ -9,9 +9,7 @@ mod query_tests;
 mod schema_introspection;
 #[cfg(test)]
 mod subscriptions;
+#[cfg(all(test, feature = "tracing"))]
+mod tracing_tests;
 #[cfg(test)]
 mod type_info_tests;
-
-#[cfg(test)]
-#[cfg(feature = "tracing")]
-mod tracing_tests;
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 289e96ae9..8dfea9e45 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -1,5 +1,4 @@
 use futures::StreamExt as _;
-use tracing::{subscriber::DefaultGuard, Level};
 
 use crate::{
     executor::Variables,
@@ -8,6 +7,7 @@ use crate::{
         schema::{Database, Query, Subscriptions},
         SpanExt as _, TestSubscriber,
     },
+    tracing::{self, subscriber::DefaultGuard, Level},
     types::scalars::EmptyMutation,
 };
 
diff --git a/juniper/src/util.rs b/juniper/src/util.rs
index 596297bfe..e6d421872 100644
--- a/juniper/src/util.rs
+++ b/juniper/src/util.rs
@@ -52,13 +52,16 @@ fn test_to_camel_case() {
     assert_eq!(&to_camel_case("")[..], "");
 }
 
-#[cfg(feature = "tracing-futures")]
+#[cfg(feature = "tracing")]
 #[doc(hidden)]
 pub mod tracing {
-    use tracing::Span;
-    use tracing_futures::{Instrument, Instrumented};
+    use crate::{
+        tracing::Span,
+        tracing_futures::{Instrument, Instrumented},
+    };
 
-    /// Required to pass sanity tests when `Instrument` already imported.
+    /// Helper trait required to pass sanity tests when [`Instrument`] is
+    /// imported already.
     pub trait InstrumentInternal {
         fn __instrument(self, span: Span) -> Instrumented<Self>
         where

From 1af4115bd61fc0d1c3503a135a34113647a39c29 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Mon, 26 Jul 2021 08:22:04 +0300
Subject: [PATCH 62/72] WIP

---
 juniper_codegen/src/graphql_interface/attr.rs |  2 +-
 .../src/graphql_interface/tracing.rs          |  2 +-
 juniper_codegen/src/lib.rs                    | 11 +++---
 juniper_codegen/src/{util => }/tracing.rs     |  0
 juniper_codegen/src/util/mod.rs               | 35 ++++++++++---------
 5 files changed, 27 insertions(+), 23 deletions(-)
 rename juniper_codegen/src/{util => }/tracing.rs (100%)

diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 3c2b4867c..669a83a9a 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -16,7 +16,7 @@ use crate::{
 };
 
 #[cfg(feature = "tracing")]
-use crate::util::tracing;
+use crate::tracing;
 
 use super::{
     inject_async_trait, ArgumentMeta, Definition, EnumType, Field, FieldArgument, ImplMeta,
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
index d931699bb..3d32055bb 100644
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -1,4 +1,4 @@
-pub use crate::util::tracing::{
+pub use crate::tracing::{
     async_tokens, instrument, span_tokens, sync_tokens, Attr, Rule, TracedArgument, TracedField,
     TracedType,
 };
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index 605a7839f..9ec5996c5 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -7,6 +7,11 @@
 #![doc(html_root_url = "https://docs.rs/juniper_codegen/0.15.7")]
 #![recursion_limit = "1024"]
 
+use proc_macro::TokenStream;
+
+use proc_macro_error::{proc_macro_error, ResultExt as _};
+use result::GraphQLScope;
+
 // NOTICE: Unfortunately this macro MUST be defined here, in the crate's root module, because Rust
 //         doesn't allow to export `macro_rules!` macros from a `proc-macro` crate type currently,
 //         and so we cannot move the definition into a sub-module and use the `#[macro_export]`
@@ -135,10 +140,8 @@ mod impl_scalar;
 mod common;
 mod graphql_interface;
 mod graphql_union;
-
-use proc_macro::TokenStream;
-use proc_macro_error::{proc_macro_error, ResultExt as _};
-use result::GraphQLScope;
+#[cfg(feature = "tracing")]
+pub mod tracing;
 
 #[proc_macro_error]
 #[proc_macro_derive(GraphQLEnum, attributes(graphql))]
diff --git a/juniper_codegen/src/util/tracing.rs b/juniper_codegen/src/tracing.rs
similarity index 100%
rename from juniper_codegen/src/util/tracing.rs
rename to juniper_codegen/src/tracing.rs
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index b4cfc6d82..67b231dd2 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -1,12 +1,5 @@
 #![allow(clippy::single_match)]
 
-pub mod duplicate;
-pub mod parse_impl;
-pub mod span_container;
-
-#[cfg(feature = "tracing")]
-pub mod tracing;
-
 use std::{collections::HashMap, str::FromStr};
 
 use proc_macro2::{Span, TokenStream};
@@ -14,15 +7,21 @@ use proc_macro_error::abort;
 use quote::quote;
 use span_container::SpanContainer;
 use syn::{
+    Attribute,
     ext::IdentExt as _,
-    parse::{Parse, ParseStream},
-    parse_quote,
-    punctuated::Punctuated,
-    spanned::Spanned,
-    token, Attribute, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
+    Ident,
+    Lit,
+    Meta,
+    MetaList, MetaNameValue, NestedMeta, parse::{Parse, ParseStream}, parse_quote, punctuated::Punctuated, spanned::Spanned, token,
 };
 
 use crate::common::parse::ParseBufferExt as _;
+#[cfg(feature = "tracing")]
+use crate::tracing;
+
+pub mod duplicate;
+pub mod parse_impl;
+pub mod span_container;
 
 /// Returns the name of a type.
 /// If the type does not end in a simple ident, `None` is returned.
@@ -415,10 +414,11 @@ impl Parse for ObjectAttributes {
                     }
                 }
                 #[cfg(feature = "tracing")]
-                "trace" => {
-                    input.parse::<syn::token::Eq>()?;
-                    let val = input.parse::<syn::LitStr>()?;
-                    if let Ok(trace) = tracing::Rule::from_str(&val.value()) {
+                "tracing" => {
+                    let content;
+                    syn::parenthesized!(content in input);
+                    let val = content.parse_any_ident()?;
+                    if let Ok(trace) = tracing::Rule::from_str(&val.to_string()) {
                         output.tracing_rule = Some(trace);
                     } else {
                         return Err(syn::Error::new(val.span(), "unknown tracing skip rule"));
@@ -1960,9 +1960,10 @@ impl GraphQLTypeDefinition {
 
 #[cfg(test)]
 mod test {
-    use super::*;
     use syn::{Ident, LitStr};
 
+    use super::*;
+
     fn strs_to_strings(source: Vec<&str>) -> Vec<String> {
         source
             .iter()

From 46c76bcd1cdf7cc0dc2fa5dc24c47bb9a1102108 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Tue, 27 Jul 2021 12:17:57 +0300
Subject: [PATCH 63/72] Refactor

---
 examples/tracing_support/src/main.rs          |  12 +-
 juniper/src/lib.rs                            |   1 +
 juniper/src/tests/fixtures/tracing/mod.rs     |   9 +-
 juniper/src/tests/fixtures/tracing/schema.rs  |  74 +++++------
 juniper/src/tests/tracing_tests.rs            |  55 ++++----
 juniper_codegen/src/common/gen.rs             |   7 +-
 juniper_codegen/src/derive_enum.rs            |   5 +-
 juniper_codegen/src/derive_input_object.rs    |   7 +-
 juniper_codegen/src/derive_object.rs          |  22 +++-
 juniper_codegen/src/graphql_interface/attr.rs |   4 +-
 juniper_codegen/src/graphql_interface/mod.rs  |  62 ++++++---
 .../src/graphql_interface/tracing.rs          |  16 ++-
 juniper_codegen/src/impl_object.rs            |  20 ++-
 juniper_codegen/src/lib.rs                    |   4 +-
 juniper_codegen/src/tracing.rs                | 124 +++++++++++-------
 juniper_codegen/src/util/mod.rs               |  62 +++++----
 16 files changed, 305 insertions(+), 179 deletions(-)

diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 5908ba63e..3334f48c9 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -58,10 +58,10 @@ struct SyncTracedUser {
 
 // Only sync `fn`s will be traced if they're not marked with `#[tracing(no_trace)]`
 // it works similarly with `#[graphql_interface]`
-#[graphql_object(Context = Context, trace = "sync")]
+#[graphql_object(Context = Context, tracing(sync))]
 impl SyncTracedUser {
     // Won't be traced because it's marked with `no_trace`
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
     }
@@ -78,15 +78,15 @@ impl SyncTracedUser {
 }
 
 #[derive(Clone, Debug, GraphQLObject)]
-#[graphql(trace = "complex")]
+#[graphql(tracing(only))]
 struct ComplexDerivedUser {
     // This shouldn't be traced because it's not marked with `#[tracing(complex)]`
     id: i32,
     // This is the only field that will be traced because it's marked with `#[tracing(complex)]`
-    #[tracing(complex)]
+    #[graphql(tracing(only))]
     kind: UserKind,
     // This shouldn't be traced because of `no_trace`.
-    #[tracing(complex, no_trace)]
+    #[graphql(tracing(ignore))]
     name: String,
 }
 
@@ -112,7 +112,7 @@ impl Query {
     }
 
     /// Create guest user with the given `id` and `name`.
-    #[tracing(skip(id))] // Here we skip `id` from being recorded into spans fields
+    #[instrument(skip(id))] // Here we skip `id` from being recorded into spans fields
     fn guest(id: i32, name: String) -> User {
         User {
             id,
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 385a3e352..17013d569 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -219,6 +219,7 @@ impl<'a> fmt::Display for GraphQLError<'a> {
                     }
                     write!(f, "{}", last)?;
                 }
+                Ok(())
             }
             GraphQLError::NoOperationProvided => write!(f, "No operation provided"),
             GraphQLError::MultipleOperationsProvided => write!(f, "Multiple operations provided"),
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 10a433cb8..73ad96bbb 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -9,7 +9,6 @@ use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 use tracing_core::{span, Subscriber};
 
 use crate::tracing::{
-    self,
     field::{Field, Visit},
     span::{Attributes, Record},
     Event, Level, Metadata,
@@ -185,7 +184,13 @@ impl SubscriberAssert {
         let current_event = self.events.remove(0);
         match current_event {
             SubscriberEvent::NewSpan(span) => {
-                assert_eq!(expected.name(), span.metadata.name());
+                assert_eq!(
+                    expected.name(),
+                    span.metadata.name(),
+                    "Expected new span with name: {}, actual: {}",
+                    expected.name(),
+                    span.metadata.name(),
+                );
                 if expected.is_strict() {
                     assert_eq!(
                         expected.fields().len(),
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 91728db94..e0b5d3e23 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -201,32 +201,32 @@ impl Foo {
     }
 
     /// Sync field marked with `no_trace`.
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     fn non_traced(&self) -> &str {
         "None can trace this"
     }
 
     /// Async field marked with `no_trace`.
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     async fn async_non_traced(&self) -> &str {
         "None can trace this"
     }
 
     /// Field with multiple arguments, one of which is skipped.
-    #[tracing(skip(name))]
+    #[instrument(skip(name))]
     fn skip_argument(&self, name: String, meaning_of_life: i32) -> i32 {
         let _ = name;
         meaning_of_life
     }
 
     /// Field with its `target` being overwritten.
-    #[tracing(target = "my_target")]
+    #[instrument(target = "my_target")]
     fn target(&self) -> bool {
         true
     }
 
     /// Field with its `level` being overwritten.
-    #[tracing(level = "warn")]
+    #[instrument(level = "warn")]
     fn level(&self) -> bool {
         true
     }
@@ -251,7 +251,7 @@ pub struct Bar {
 #[graphql_object(context = Database, impl = FooBarValue)]
 impl Bar {
     /// Custom field.
-    #[tracing(fields(self.id = self.id))]
+    #[instrument(fields(self.id = self.id))]
     fn id(&self) -> i32 {
         self.id
     }
@@ -263,7 +263,7 @@ impl Bar {
 
     /// Field with default arguments.
     #[graphql(arguments(this(default = 42), another(default = 0), skipped(default = 1),))]
-    #[tracing(skip(skipped))]
+    #[instrument(skip(skipped))]
     fn default_arg(&self, this: i32, another: i32, skipped: i32) -> i32 {
         this + another + skipped
     }
@@ -295,19 +295,19 @@ impl FooBar for Bar {
 #[graphql(impl = FooBarValue, context = Database)]
 pub struct DerivedFoo {
     /// Resolver having context bound and const bound trace fields.
-    #[tracing(fields(self.id = self.id, custom_fields = "work"))]
+    #[instrument(fields(self.id = self.id, custom_fields = "work"))]
     id: i32,
 
     /// Field marked with `no_trace` within derived [`GraphQLObject`].
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     non_traced: String,
 
     /// Field with its `target` being overwritten.
-    #[tracing(target = "my_target")]
+    #[instrument(target = "my_target")]
     target: bool,
 
     /// Field with its `level` being overwritten.
-    #[tracing(level = "warn")]
+    #[instrument(level = "warn")]
     level: bool,
 }
 
@@ -332,19 +332,19 @@ pub trait FooBar {
     async fn is_bar(&self) -> bool;
 
     /// Interface field marked with `no_trace`.
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     fn non_traced(&self) -> bool {
         true
     }
 
     /// Async interface field marked with `no_trace`.
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     async fn async_non_traced(&self) -> bool {
         true
     }
 
     /// Interface field with various arguments.
-    #[tracing(skip(skipped))]
+    #[instrument(skip(skipped))]
     fn with_arg(
         &self,
         id: i32,
@@ -356,7 +356,7 @@ pub trait FooBar {
     }
 
     /// Async interface field with various arguments.
-    #[tracing(skip(skipped))]
+    #[instrument(skip(skipped))]
     async fn async_with_arg(
         &self,
         id: i32,
@@ -368,13 +368,13 @@ pub trait FooBar {
     }
 
     /// Field with overwritten `target` of span.
-    #[tracing(target = "my_target")]
+    #[instrument(target = "my_target")]
     fn target(&self) -> i32 {
         1
     }
 
     /// Field with overwritten `level` of span.
-    #[tracing(level = "warn")]
+    #[instrument(level = "warn")]
     fn level(&self) -> i32 {
         2
     }
@@ -384,7 +384,7 @@ pub trait FooBar {
 pub struct TraceSync;
 
 #[graphql_object(
-    trace = "sync",
+    tracing(sync),
     impl = [InterfacedSimpleValue, InterfacedSyncValue],
 )]
 impl TraceSync {
@@ -402,7 +402,7 @@ build_impl!(TraceSync, InterfacedSync);
 
 /// Derived GraphQL object marked with `trace = "sync"`.
 #[derive(Default, GraphQLObject)]
-#[graphql(trace = "sync")]
+#[graphql(tracing(sync))]
 pub struct SyncDerived {
     /// Simple field
     sync: i32,
@@ -412,7 +412,7 @@ pub struct SyncDerived {
 pub struct TraceAsync;
 
 #[graphql_object(
-    trace = "async",
+    tracing(async),
     impl = [InterfacedAsyncValue],
 )]
 impl TraceAsync {
@@ -429,7 +429,7 @@ build_impl!(TraceAsync, InterfacedAsync);
 
 /// Derived GraphQL object.
 #[derive(Default, GraphQLObject)]
-#[graphql(trace = "async")]
+#[graphql(tracing(async))]
 pub struct AsyncDerived {
     /// Simple field
     sync: i32,
@@ -439,7 +439,7 @@ pub struct AsyncDerived {
 pub struct SkipAll;
 
 #[graphql_object(
-    trace = "skip-all",
+    tracing(skip_all),
     impl = [InterfacedSkipAllValue],
 )]
 impl SkipAll {
@@ -456,7 +456,7 @@ build_impl!(SkipAll, InterfacedSkipAll);
 
 /// Derived GraphQL object marked with `trace = "skip-all"`.
 #[derive(Default, GraphQLObject)]
-#[graphql(trace = "skip-all")]
+#[graphql(tracing(skip_all))]
 pub struct SkipAllDerived {
     /// Simple field
     sync: i32,
@@ -466,16 +466,16 @@ pub struct SkipAllDerived {
 pub struct Complex;
 
 #[graphql_object(
-    trace = "complex",
+    tracing(only),
     impl = [InterfacedComplexValue],
 )]
 impl Complex {
-    #[tracing(complex)]
+    #[graphql(tracing(only))]
     pub fn sync_fn(&self) -> i32 {
         1
     }
 
-    #[tracing(complex)]
+    #[graphql(tracing(only))]
     pub async fn async_fn(&self) -> i32 {
         2
     }
@@ -483,22 +483,18 @@ impl Complex {
     fn simple_field(&self) -> i32 {
         3
     }
-
-    #[tracing(complex, no_trace)]
-    fn no_trace_complex(&self) -> i32 {
-        4
-    }
 }
 
 build_impl!(Complex, InterfacedComplex);
 
 /// Derived GraphQL object marked with `trace = "complex"`.
 #[derive(GraphQLObject)]
-#[graphql(trace = "complex")]
+#[graphql(tracing(only))]
 pub struct DerivedComplex {
-    #[tracing(complex)]
+    #[graphql(tracing(only))]
     complex: bool,
-    #[tracing(complex, fields(test = "magic"))]
+    #[graphql(tracing(only))]
+    #[instrument(fields(test = "magic"))]
     another_complex: bool,
 
     /// Simple field
@@ -516,7 +512,7 @@ trait InterfacedSimple {
 
 #[graphql_interface(
     for = [TraceSync],
-    trace = "sync",
+    tracing(sync),
     async,
 )]
 trait InterfacedSync {
@@ -526,7 +522,7 @@ trait InterfacedSync {
 
 #[graphql_interface(
     for = [TraceAsync],
-    trace = "async",
+    tracing(async),
     async,
 )]
 trait InterfacedAsync {
@@ -536,7 +532,7 @@ trait InterfacedAsync {
 
 #[graphql_interface(
     for = [SkipAll],
-    trace = "skip-all",
+    tracing(skip_all),
     async,
 )]
 trait InterfacedSkipAll {
@@ -546,11 +542,11 @@ trait InterfacedSkipAll {
 
 #[graphql_interface(
     for = [Complex],
-    trace = "complex",
+    tracing(only),
     async,
 )]
 trait InterfacedComplex {
     fn sync_fn(&self) -> i32;
-    #[tracing(complex)]
+    #[graphql(tracing(only))]
     async fn async_fn(&self) -> i32;
 }
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 8dfea9e45..0a7968478 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -45,7 +45,7 @@ fn test_execute_sync_clean() {
     handle
         .assert()
         .enter_new_span("execute_sync")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
         .enter_new_span("execute_validated_query")
         .enter_new_span("Query.foo")
@@ -77,10 +77,10 @@ fn test_execute_sync_with_error() {
     handle
         .assert()
         .enter_new_span("execute_sync")
-        .enter_new_span("rule_validation")
+        .enter_new_span("validate_document")
         // Test that it writes event to traces when failed to validate rules
         .event(Level::TRACE, Some("juniper"), vec![])
-        .close_exited("rule_validation")
+        .close_exited("validate_document")
         .close_exited("execute_sync");
 }
 
@@ -104,9 +104,9 @@ async fn records_sub_spans() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.bar")
         // To check whether context hasn't leaked.
         .enter_new_span(&"Bar.tracedData".with_strict_fields(true))
@@ -116,7 +116,7 @@ async fn records_sub_spans() {
         // Should has no child spans because `non_traced_query` doesn't marked with `#[instrument]`
         .simple_span(&"Bar.nonTracedData".with_strict_fields(true))
         .close_exited("Query.bar")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -149,9 +149,9 @@ async fn test_no_trace_field() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.foo")
         // In between this two steps should be no other, because `nonTraced` and `asyncNonTraced`
         // marked with `no_trace`
@@ -163,7 +163,7 @@ async fn test_no_trace_field() {
         // Field with name present in interface but resolved on concrete object.
         .simple_span("Bar.nonTraced")
         .close_exited("Query.fooBars")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -188,9 +188,9 @@ async fn test_impl_fn_args() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.foo")
         // Skipped field
         .simple_span(
@@ -210,7 +210,7 @@ async fn test_impl_fn_args() {
                 .with_strict_fields(true),
         )
         .close_exited("Query.bar")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -236,9 +236,9 @@ async fn test_custom_fields() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span(&"Query.bar".with_field("id", "127").with_strict_fields(true))
         // Check whether custom field "self.id" exists
         .simple_span(
@@ -256,7 +256,7 @@ async fn test_custom_fields() {
                 .with_strict_fields(true),
         )
         .close_exited("Query.asyncFoo")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -288,9 +288,9 @@ async fn overwrite_level_and_target() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.foo")
         .simple_span(
             &"Foo.target"
@@ -339,7 +339,7 @@ async fn overwrite_level_and_target() {
                 .with_level(Level::WARN),
         )
         .close_exited("Query.fooBar")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -372,7 +372,6 @@ async fn graphql_object_trace_arg() {
                 syncFn
                 asyncFn
                 simpleField
-                noTraceComplex
             }
             complexDerived {
                 complex
@@ -386,14 +385,14 @@ async fn graphql_object_trace_arg() {
     let (handle, _guard) = init_tracer();
 
     let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
-    assert!(res.is_ok(), "Should be ok");
+    assert!(res.is_ok(), "Should be ok, Err: {:?}", res.err().unwrap());
 
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.traceAsync")
         .simple_span("TraceAsync.asyncFn")
         // There shouldn't be span for `syncFn` because it's not async
@@ -413,7 +412,7 @@ async fn graphql_object_trace_arg() {
         .enter_new_span("Query.complexSync")
         .simple_span("Complex.syncFn")
         .simple_span("Complex.asyncFn")
-        // There shouldn't be any spans for `simpleField` and `noTraceComplex`
+        // There shouldn't be any span for `simpleField`
         .close_exited("Query.complexSync")
         .enter_new_span("Query.complexDerived")
         .simple_span("DerivedComplex.complex")
@@ -424,7 +423,7 @@ async fn graphql_object_trace_arg() {
         )
         // There shouldn't be span for `sync` because it's not marked with `#[tracing(complex)]`
         .close_exited("Query.complexDerived")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -464,9 +463,9 @@ async fn graphql_interface_trace_arg() {
     handle
         .assert()
         .enter_new_span("execute")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
-        .enter_new_span("execute_validated_query")
+        .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.erasedSimple")
         .simple_span("InterfacedSimple.syncFn")
         .simple_span("InterfacedSimple.asyncFn")
@@ -481,7 +480,7 @@ async fn graphql_interface_trace_arg() {
         .enter_new_span("Query.erasedComplex")
         .simple_span("InterfacedComplex.asyncFn")
         .close_exited("Query.erasedComplex")
-        .close_exited("execute_validated_query")
+        .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
 
@@ -543,7 +542,7 @@ async fn subscription_tracing() {
     handle
         .assert()
         .enter_new_span("resolve_into_stream")
-        .simple_span("rule_validation")
+        .simple_span("validate_document")
         .simple_span("validate_input_values")
         .enter_new_span("resolve_validated_subscription")
         .new_span(
diff --git a/juniper_codegen/src/common/gen.rs b/juniper_codegen/src/common/gen.rs
index 52e1d8535..d3be04c7f 100644
--- a/juniper_codegen/src/common/gen.rs
+++ b/juniper_codegen/src/common/gen.rs
@@ -39,7 +39,7 @@ pub(crate) fn async_resolving_code(
     let ty = ty.map(|t| quote! { : #t });
 
     quote! {
-        Box::pin(::juniper::futures::FutureExt::then(fut, move |res #ty| async move {
+        let f = ::juniper::futures::FutureExt::then(fut, move |res #ty| async move {
             match ::juniper::IntoResolvable::into(res, executor.context())? {
                 Some((ctx, r)) => {
                     let subexec = executor.replaced_context(ctx);
@@ -47,6 +47,9 @@ pub(crate) fn async_resolving_code(
                 },
                 None => Ok(::juniper::Value::null()),
             }
-        }) #trace_async)
+        });
+
+        #trace_async
+        Box::pin(f)
     }
 }
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index 273568e66..8fee73aad 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -100,8 +100,11 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
                 default: None,
                 span,
 
+                // Enums cannot be traced.
                 #[cfg(feature = "tracing")]
-                tracing: field_attrs.tracing,
+                tracing_behaviour: None,
+                #[cfg(feature = "tracing")]
+                instrument_attr: None,
             })
         })
         .collect::<Vec<_>>();
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index a3f4822ce..3d4149c0e 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -96,8 +96,11 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
                 default,
                 span,
 
+                // Tracing of GraphQL Input Objects not supported
                 #[cfg(feature = "tracing")]
-                tracing: field_attrs.tracing,
+                tracing_behaviour: None,
+                #[cfg(feature = "tracing")]
+                instrument_attr: None,
             })
         })
         .collect::<Vec<_>>();
@@ -150,7 +153,7 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
         no_async: attrs.no_async.is_some(),
 
         #[cfg(feature = "tracing")]
-        tracing_rule: attrs.tracing_rule,
+        tracing_rule: None,
     };
 
     Ok(definition.into_input_object_tokens())
diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs
index 8d53c5d6c..fee0e991e 100644
--- a/juniper_codegen/src/derive_object.rs
+++ b/juniper_codegen/src/derive_object.rs
@@ -6,6 +6,9 @@ use proc_macro2::TokenStream;
 use quote::quote;
 use syn::{self, ext::IdentExt, spanned::Spanned, Data, Fields};
 
+#[cfg(feature = "tracing")]
+use crate::tracing;
+
 pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<TokenStream> {
     let ast_span = ast.span();
     let struct_fields = match ast.data {
@@ -41,6 +44,21 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
                 }
             };
 
+            #[cfg(feature = "tracing")]
+            let instrument_attr = match field
+                .attrs
+                .iter()
+                .find(|attr| attr.path.is_ident(tracing::ATTR_NAME))
+                .map(|attr| attr.parse_args())
+                .transpose()
+            {
+                Ok(attr) => attr,
+                Err(err) => {
+                    proc_macro_error::emit_error!(err);
+                    None
+                }
+            };
+
             if field_attrs.skip.is_some() {
                 return None;
             }
@@ -89,7 +107,9 @@ pub fn build_derive_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::R
                 span,
 
                 #[cfg(feature = "tracing")]
-                tracing: field_attrs.tracing,
+                tracing_behaviour: field_attrs.tracing,
+                #[cfg(feature = "tracing")]
+                instrument_attr,
             })
         })
         .collect::<Vec<_>>();
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 669a83a9a..cb761cfd5 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -509,7 +509,9 @@ impl TraitMethod {
             is_async: method.sig.asyncness.is_some(),
 
             #[cfg(feature = "tracing")]
-            tracing: tracing::Attr::from_method(method),
+            instrument: tracing::Attr::from_trait_method(method),
+            #[cfg(feature = "tracing")]
+            tracing: meta.tracing_behaviour.map(|t| t.into_inner()),
         })
     }
 
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index 448e3990c..ac4b546c3 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -231,15 +231,16 @@ impl Parse for TraitMeta {
                     output.is_internal = true;
                 }
                 #[cfg(feature = "tracing")]
-                "trace" => {
+                "tracing" => {
                     use std::str::FromStr as _;
 
                     use proc_macro_error::abort;
 
                     let span = ident.span();
-                    input.parse::<token::Eq>()?;
-                    let tracing = input.parse::<syn::LitStr>()?;
-                    let tracing_rule = tracing::Rule::from_str(tracing.value().as_str());
+                    let content;
+                    syn::parenthesized!(content in input);
+                    let tracing = content.parse_any_ident()?;
+                    let tracing_rule = tracing::Rule::from_str(tracing.to_string().as_str());
                     match tracing_rule {
                         Ok(rule) => output
                             .tracing_rule
@@ -249,8 +250,8 @@ impl Parse for TraitMeta {
                             tracing.span(),
                             format!(
                                 "Unknown tracing rule: {}, \
-                                 known values: trace-sync, trace-async, skip-all and complex",
-                                tracing.value(),
+                                 known values: sync, async, skip-all and complex",
+                                tracing,
                             )
                         )),
                     }
@@ -452,6 +453,12 @@ struct MethodMeta {
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     /// [2]: https://spec.graphql.org/June2018/#sec-Objects
     downcast: Option<SpanContainer<syn::Ident>>,
+
+    /// Explicitly specified tracing behaviour of this [GraphQL field][1].
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    #[cfg(feature = "tracing")]
+    tracing_behaviour: Option<SpanContainer<tracing::FieldBehaviour>>,
 }
 
 impl Parse for MethodMeta {
@@ -500,6 +507,21 @@ impl Parse for MethodMeta {
                     .downcast
                     .replace(SpanContainer::new(ident.span(), None, ident.clone()))
                     .none_or_else(|_| err::dup_arg(&ident))?,
+                #[cfg(feature = "tracing")]
+                "tracing" => {
+                    let content;
+                    syn::parenthesized!(content in input);
+                    let val = content.parse_any_ident()?;
+                    let behaviour = tracing::FieldBehaviour::from_ident(&val)?;
+                    output
+                        .tracing_behaviour
+                        .replace(SpanContainer::new(
+                            ident.span(),
+                            Some(val.span()),
+                            behaviour,
+                        ))
+                        .none_or_else(|_| err::dup_arg(&ident))?;
+                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -520,6 +542,8 @@ impl MethodMeta {
             deprecated: try_merge_opt!(deprecated: self, another),
             ignore: try_merge_opt!(ignore: self, another),
             downcast: try_merge_opt!(downcast: self, another),
+            #[cfg(feature = "tracing")]
+            tracing_behaviour: try_merge_opt!(tracing_behaviour: self, another),
         })
     }
 
@@ -1016,8 +1040,6 @@ impl Definition {
             .filter_map(|i| i.method_resolve_into_type_async_tokens(&trait_ty));
         let regular_downcast = self.ty.method_resolve_into_type_async_tokens();
 
-        let instrument = if_tracing_enabled!(tracing::instrument());
-
         quote! {
             #[automatically_derived]
             impl#impl_generics ::juniper::GraphQLValueAsync<#scalar> for #ty #where_clause
@@ -1029,8 +1051,6 @@ impl Definition {
                     args: &'b ::juniper::Arguments<#scalar>,
                     executor: &'b ::juniper::Executor<Self::Context, #scalar>,
                 ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<#scalar>> {
-                    #instrument
-
                     match field {
                         #( #fields_resolvers )*
                         _ => #no_field_panic,
@@ -1342,14 +1362,23 @@ struct Field {
     /// Tracing attribute placed on this [GraphQL field][2]. Only relevant when `tracing`
     /// feature is enabled.
     ///
-    /// If it is present and `tracing` feature is enabled it can be used to alter trace
-    /// behaviour.
+    /// If it is present and `tracing` feature is enabled it can be used to alter traces,
+    /// generated for this [GraphQL field][2].
     ///
-    /// It it is present and `tracing` feature is disabled it will cause compile time error.
+    /// If it is present and `tracing` feature is disabled it will result in compile time
+    /// error.
     ///
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
-    tracing: Option<tracing::Attr>,
+    instrument: Option<tracing::Attr>,
+
+    /// Tracing behaviour for this [GraphQL field][2] parsed from `#[graphql(tracing = ...)]`
+    ///
+    /// It can be used to define whether this field should be traced.
+    ///
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    #[cfg(feature = "tracing")]
+    tracing: Option<tracing::FieldBehaviour>,
 }
 
 impl Field {
@@ -1639,7 +1668,10 @@ impl Implementer {
         Some(quote! {
             if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
                 let fut = ::juniper::futures::future::ready(#downcast);
-                return #resolving_code;
+                let f = {
+                    #resolving_code
+                };
+                return f;
             }
         })
     }
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
index 3d32055bb..1221528f5 100644
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -1,13 +1,13 @@
 pub use crate::tracing::{
-    async_tokens, instrument, span_tokens, sync_tokens, Attr, Rule, TracedArgument, TracedField,
-    TracedType,
+    async_tokens, span_tokens, sync_tokens, Attr, FieldBehaviour, Rule, TracedArgument,
+    TracedField, TracedType,
 };
 
 use super::{Definition, Field, FieldArgument};
 
 impl TracedType for Definition {
-    fn tracing_rule(&self) -> Option<Rule> {
-        self.tracing_rule
+    fn tracing_rule(&self) -> Rule {
+        self.tracing_rule.unwrap_or(Rule::All)
     }
 
     fn name(&self) -> &str {
@@ -22,8 +22,12 @@ impl TracedType for Definition {
 impl TracedField for Field {
     type Arg = FieldArgument;
 
-    fn tracing_attr(&self) -> Option<&Attr> {
-        self.tracing.as_ref()
+    fn instrument(&self) -> Option<&Attr> {
+        self.instrument.as_ref()
+    }
+
+    fn tracing_behaviour(&self) -> FieldBehaviour {
+        self.tracing.unwrap_or(FieldBehaviour::Default)
     }
 
     fn is_async(&self) -> bool {
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 34d632636..538643886 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -8,6 +8,9 @@ use proc_macro2::TokenStream;
 use quote::quote;
 use syn::{ext::IdentExt, spanned::Spanned};
 
+#[cfg(feature = "tracing")]
+use crate::tracing;
+
 /// Generate code for the juniper::graphql_object macro.
 pub fn build_object(args: TokenStream, body: TokenStream, error: GraphQLScope) -> TokenStream {
     let definition = match create(args, body, error) {
@@ -79,6 +82,19 @@ fn create(
                 }
             };
 
+            #[cfg(feature = "tracing")]
+            let instrument_attr = match method.attrs
+                .iter()
+                .find(|attr| attr.path.is_ident(tracing::ATTR_NAME))
+                .map(|attr| attr.parse_args())
+                .transpose() {
+                Ok(attr) => attr,
+                Err(err) => {
+                    proc_macro_error::emit_error!(err);
+                    return None;
+                }
+            };
+
             let parse_method =
                 _impl.parse_method(&method, true, |captured, arg_ident, is_mut: bool| {
                     let arg_name = arg_ident.unraw().to_string();
@@ -191,7 +207,9 @@ fn create(
                 span,
 
                 #[cfg(feature = "tracing")]
-                tracing: attrs.tracing,
+                tracing_behaviour: attrs.tracing,
+                #[cfg(feature = "tracing")]
+                instrument_attr,
             })
         })
         .collect::<Vec<_>>();
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index 9ec5996c5..f68a40ce4 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -141,7 +141,7 @@ mod common;
 mod graphql_interface;
 mod graphql_union;
 #[cfg(feature = "tracing")]
-pub mod tracing;
+mod tracing;
 
 #[proc_macro_error]
 #[proc_macro_derive(GraphQLEnum, attributes(graphql))]
@@ -166,7 +166,7 @@ pub fn derive_input_object(input: TokenStream) -> TokenStream {
 }
 
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLObject, attributes(graphql, tracing))]
+#[proc_macro_derive(GraphQLObject, attributes(graphql, instrument))]
 pub fn derive_object(input: TokenStream) -> TokenStream {
     let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
     let gen = derive_object::build_derive_object(ast, GraphQLScope::DeriveObject);
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index 212969085..e76d92c26 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -9,6 +9,8 @@ use syn::{
     token,
 };
 
+pub const ATTR_NAME: &'static str = "instrument";
+
 #[derive(Debug, Default)]
 pub struct Attr {
     /// Optional span rename, if `None` method name should be used instead.
@@ -25,28 +27,22 @@ pub struct Attr {
 
     /// Custom fields.
     fields: Vec<syn::ExprAssign>,
-
-    /// Whether this field is marked with `#[tracing(complex)]`
-    is_complex: bool,
-
-    /// Whether this field is marked with `#[tracing(no_trace)]`
-    no_trace: bool,
 }
 
 impl Attr {
     /// Parses [`TracingAttr`] from `method`s attributes and removes itself from
     /// `method.attrs` if present.
-    pub fn from_method(method: &mut syn::TraitItemMethod) -> Option<Self> {
+    pub fn from_trait_method(method: &mut syn::TraitItemMethod) -> Option<Self> {
         let attr = method
             .attrs
             .iter()
-            .find(|attr| attr.path.is_ident("tracing"))
+            .find(|attr| attr.path.is_ident(&ATTR_NAME))
             .map(|attr| attr.parse_args())
             .transpose();
 
         method.attrs = mem::take(&mut method.attrs)
             .into_iter()
-            .filter(|attr| !attr.path.is_ident("tracing"))
+            .filter(|attr| !attr.path.is_ident(&ATTR_NAME))
             .collect();
 
         match attr {
@@ -86,12 +82,6 @@ impl Parse for Attr {
                         skipped_fields.parse::<token::Comma>().ok();
                     }
                 }
-                "no_trace" => {
-                    attr.no_trace = true;
-                }
-                "complex" => {
-                    attr.is_complex = true;
-                }
                 "fields" => {
                     let fields;
                     syn::parenthesized!(fields in input);
@@ -114,6 +104,9 @@ impl Parse for Attr {
 /// The different possible groups of fields to trace.
 #[derive(Copy, Clone, Debug)]
 pub enum Rule {
+    /// Trace all fields.
+    All,
+
     /// Trace all fields that resolved using `async fn`s.
     Async,
 
@@ -127,14 +120,9 @@ pub enum Rule {
     SkipAll,
 }
 
-impl Rule {
-    pub fn is_traced(&self, field: &impl TracedField) -> bool {
-        match self {
-            Self::Async => field.is_async(),
-            Self::Sync => !field.is_async(),
-            Self::Complex => field.tracing_attr().map_or(false, |t| t.is_complex),
-            Self::SkipAll => false,
-        }
+impl Default for Rule {
+    fn default() -> Self {
+        Self::All
     }
 }
 
@@ -145,19 +133,51 @@ impl FromStr for Rule {
         match rule {
             "async" => Ok(Self::Async),
             "sync" => Ok(Self::Sync),
-            "skip-all" => Ok(Self::SkipAll),
-            "complex" => Ok(Self::Complex),
+            "skip_all" => Ok(Self::SkipAll),
+            "only" => Ok(Self::Complex),
             _ => Err(()),
         }
     }
 }
 
+/// Marker on field which used together with [`Rule`] to decide whether this
+/// field should be traced.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum FieldBehaviour {
+    /// Default tracing behaviour.
+    ///
+    /// It means that field **should** be traced if nothing else restricting it.
+    Default,
+
+    /// Used together with `tracing(only)` argument to mark that field should be traced.
+    Only,
+
+    /// Used to mark that field shouldn't be traced at all.
+    Ignore,
+}
+
+impl FieldBehaviour {
+    pub fn from_ident(ident: &syn::Ident) -> syn::Result<Self> {
+        match ident.to_string().as_str() {
+            "only" => Ok(Self::Only),
+            "ignore" | "skip" => Ok(Self::Ignore),
+            _ => Err(syn::Error::new(
+                ident.span(),
+                format!(
+                    "Unknown tracing behaviour: got {}, supported values: only, ignore, skip",
+                    ident,
+                ),
+            )),
+        }
+    }
+}
+
 /// Generalisation of type that can be traced.
 pub trait TracedType {
     /// Optional [`TracingRule`] read from attributes `#[graphql_object(trace = "...")]`
     /// on impl block, `#[graphql(trace = "...")]` on derived GraphQLObject or
     /// `#[graphql_interface(trace = "...")]` on trait definition.
-    fn tracing_rule(&self) -> Option<Rule>;
+    fn tracing_rule(&self) -> Rule;
 
     /// Name of this type.
     fn name(&self) -> &str;
@@ -172,7 +192,10 @@ pub trait TracedField {
     type Arg: TracedArgument;
 
     /// Returns parsed `#[tracing]` attribute.
-    fn tracing_attr(&self) -> Option<&Attr>;
+    fn instrument(&self) -> Option<&Attr>;
+
+    /// Returns [`FieldBehaviour`] parsed from `#[graphql(tracing = ...)]`
+    fn tracing_behaviour(&self) -> FieldBehaviour;
 
     /// Whether this field relies on async resolver.
     fn is_async(&self) -> bool;
@@ -191,19 +214,15 @@ pub trait TracedArgument {
 }
 
 fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
-    let traced = ty
-        .tracing_rule()
-        .map_or_else(|| true, |rule| rule.is_traced(field));
-
-    let no_trace = field.tracing_attr().map(|t| t.no_trace).unwrap_or(false);
-
-    traced && !no_trace
-}
-
-pub fn instrument() -> TokenStream {
-    quote!(
-        use ::juniper::InstrumentInternal as _;
-    )
+    let rule = ty.tracing_rule();
+
+    match rule {
+        Rule::All => field.tracing_behaviour() != FieldBehaviour::Ignore,
+        Rule::Sync if !field.is_async() => field.tracing_behaviour() != FieldBehaviour::Ignore,
+        Rule::Async if field.is_async() => field.tracing_behaviour() != FieldBehaviour::Ignore,
+        Rule::Complex => field.tracing_behaviour() == FieldBehaviour::Only,
+        _ => false,
+    }
 }
 
 // Returns code that constructs `span` required for tracing
@@ -222,7 +241,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
         let arg_name = syn::LitStr::new(name, arg.ty().span());
 
         field
-            .tracing_attr()
+            .instrument()
             .map(|t| t.skip.get(&raw_name.to_string()))
             .flatten()
             .is_none()
@@ -233,7 +252,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
             })
     });
 
-    let args: Vec<_> = if let Some(tracing) = field.tracing_attr() {
+    let args: Vec<_> = if let Some(tracing) = field.instrument() {
         let additional_fields = tracing.fields.iter().map(|f| {
             let name = &f.left;
             let right = &f.right;
@@ -246,7 +265,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     };
 
     let level = field
-        .tracing_attr()
+        .instrument()
         .map(|t| t.level.as_ref())
         .flatten()
         .map(|l| match l.value().as_str() {
@@ -267,7 +286,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
         .unwrap_or_else(|| quote!(INFO));
 
     let target = field
-        .tracing_attr()
+        .instrument()
         .map(|t| t.target.as_ref())
         .flatten()
         .map_or_else(|| quote!(), |t| quote!(target: #t,));
@@ -284,7 +303,9 @@ pub fn async_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStre
     if !is_traced(ty, field) {
         return quote!();
     }
-    quote!(.__instrument(_tracing_span))
+    quote! (
+        let f = <_ as ::juniper::tracing_futures::Instrument>::instrument(f, _tracing_span);
+    )
 }
 
 // Returns code to start tracing of sync block
@@ -301,10 +322,11 @@ mod graphql_object {
     };
 
     use super::{Attr, Rule, TracedArgument, TracedField, TracedType};
+    use crate::tracing::FieldBehaviour;
 
     impl TracedType for GraphQLTypeDefinition {
-        fn tracing_rule(&self) -> Option<Rule> {
-            self.tracing_rule
+        fn tracing_rule(&self) -> Rule {
+            self.tracing_rule.unwrap_or(Rule::All)
         }
 
         fn name(&self) -> &str {
@@ -319,8 +341,12 @@ mod graphql_object {
     impl TracedField for GraphQLTypeDefinitionField {
         type Arg = GraphQLTypeDefinitionFieldArg;
 
-        fn tracing_attr(&self) -> Option<&Attr> {
-            self.tracing.as_ref()
+        fn instrument(&self) -> Option<&Attr> {
+            self.instrument_attr.as_ref()
+        }
+
+        fn tracing_behaviour(&self) -> FieldBehaviour {
+            self.tracing_behaviour.unwrap_or(FieldBehaviour::Default)
         }
 
         fn name(&self) -> &str {
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 67b231dd2..ff4e4e131 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -7,12 +7,12 @@ use proc_macro_error::abort;
 use quote::quote;
 use span_container::SpanContainer;
 use syn::{
-    Attribute,
     ext::IdentExt as _,
-    Ident,
-    Lit,
-    Meta,
-    MetaList, MetaNameValue, NestedMeta, parse::{Parse, ParseStream}, parse_quote, punctuated::Punctuated, spanned::Spanned, token,
+    parse::{Parse, ParseStream},
+    parse_quote,
+    punctuated::Punctuated,
+    spanned::Spanned,
+    token, Attribute, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
 };
 
 use crate::common::parse::ParseBufferExt as _;
@@ -516,6 +516,8 @@ enum FieldAttribute {
     Skip(SpanContainer<syn::Ident>),
     Arguments(HashMap<String, FieldAttributeArgument>),
     Default(Box<SpanContainer<Option<syn::Expr>>>),
+    #[cfg(feature = "tracing")]
+    Tracing(SpanContainer<tracing::FieldBehaviour>),
 }
 
 impl Parse for FieldAttribute {
@@ -590,6 +592,19 @@ impl Parse for FieldAttribute {
 
                 Ok(FieldAttribute::Default(Box::new(default_expr)))
             }
+            #[cfg(feature = "tracing")]
+            "tracing" => {
+                let content;
+                syn::parenthesized!(content in input);
+                let behaviour = content.parse_any_ident()?;
+                tracing::FieldBehaviour::from_ident(&behaviour).map(|val| {
+                    FieldAttribute::Tracing(SpanContainer::new(
+                        ident.span(),
+                        Some(behaviour.span()),
+                        val,
+                    ))
+                })
+            }
             _ => Err(syn::Error::new(ident.span(), "unknown attribute")),
         }
     }
@@ -606,9 +621,10 @@ pub struct FieldAttributes {
     pub arguments: HashMap<String, FieldAttributeArgument>,
     /// Only relevant for object input objects.
     pub default: Option<SpanContainer<Option<syn::Expr>>>,
-    // Only relevant when `tracing` feature enabled
+
+    // Only relevant for GraphQLObject derive and graphql_object attribute.
     #[cfg(feature = "tracing")]
-    pub tracing: Option<tracing::Attr>,
+    pub tracing: Option<tracing::FieldBehaviour>,
 }
 
 impl Parse for FieldAttributes {
@@ -637,6 +653,8 @@ impl Parse for FieldAttributes {
                 FieldAttribute::Default(expr) => {
                     output.default = Some(*expr);
                 }
+                #[cfg(feature = "tracing")]
+                FieldAttribute::Tracing(tracing) => output.tracing = Some(*tracing),
             }
         }
 
@@ -671,11 +689,6 @@ impl FieldAttributes {
             output.deprecation = deprecation;
         }
 
-        #[cfg(feature = "tracing")]
-        if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("tracing")) {
-            output.tracing = Some(attr.parse_args()?);
-        }
-
         Ok(output)
     }
 
@@ -707,8 +720,11 @@ pub struct GraphQLTypeDefinitionField {
     pub default: Option<TokenStream>,
     pub span: Span,
 
+    // Relevant only for `#[graphql_object]` and `#[derive(GraphQLObject)]`
     #[cfg(feature = "tracing")]
-    pub tracing: Option<tracing::Attr>,
+    pub tracing_behaviour: Option<tracing::FieldBehaviour>,
+    #[cfg(feature = "tracing")]
+    pub instrument_attr: Option<tracing::Attr>,
 }
 
 impl syn::spanned::Spanned for GraphQLTypeDefinitionField {
@@ -751,6 +767,7 @@ pub struct GraphQLTypeDefinition {
     // FIXME: make this redundant.
     pub no_async: bool,
 
+    // Only relevant for GraphQL Objects.
     #[cfg(feature = "tracing")]
     pub tracing_rule: Option<tracing::Rule>,
 }
@@ -928,8 +945,6 @@ impl GraphQLTypeDefinition {
         };
         let (impl_generics, _, where_clause) = generics.split_for_impl();
 
-        let tracing_instrument = if_tracing_enabled!(tracing::instrument());
-
         let resolve_field_async = {
             let resolve_matches_async = self.fields.iter().map(|field| {
                 let name = &field.name;
@@ -969,7 +984,8 @@ impl GraphQLTypeDefinition {
                                     Ok(None) => Ok(::juniper::Value::null()),
                                     Err(e) => Err(e),
                                 }
-                            } #trace_async;
+                            };
+                            #trace_async;
                             Box::pin(f)
                         },
                     )
@@ -988,7 +1004,8 @@ impl GraphQLTypeDefinition {
                                     Ok(None) => Ok(::juniper::Value::null()),
                                     Err(e) => Err(e),
                                 }
-                            } #trace_async;
+                            };
+                            #trace_async;
                             use ::juniper::futures::future;
                             future::FutureExt::boxed(f)
                         )
@@ -1071,7 +1088,6 @@ impl GraphQLTypeDefinition {
                     {
                         use ::juniper::futures::future;
                         use ::juniper::GraphQLType;
-                        #tracing_instrument;
                         match field {
                             #( #resolve_matches_async )*
                             _ => {
@@ -1159,8 +1175,6 @@ impl GraphQLTypeDefinition {
                     args: &::juniper::Arguments<#scalar>,
                     executor: &::juniper::Executor<Self::Context, #scalar>,
                 ) -> ::juniper::ExecutionResult<#scalar> {
-                    #tracing_instrument
-
                     match field {
                         #( #resolve_matches )*
                         _ => {
@@ -1366,7 +1380,10 @@ impl GraphQLTypeDefinition {
                                         Err(e) => Err(ex.new_error(e)),
                                     }
                                 }
-                            }) #trace_async;
+                            });
+
+                            #trace_async;
+
                             Ok(
                                 ::juniper::Value::Scalar::<
                                     ::juniper::ValuesStream::<#scalar>
@@ -1459,8 +1476,6 @@ impl GraphQLTypeDefinition {
             }
         );
 
-        let instrument = if_tracing_enabled!(tracing::instrument());
-
         let subscription_implementation = quote!(
             impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens
             #where_clause_with_send_sync
@@ -1493,7 +1508,6 @@ impl GraphQLTypeDefinition {
                 {
                     use ::juniper::Value;
                     use ::juniper::futures::stream::StreamExt as _;
-                    #instrument
 
                     match field_name {
                             #( #resolve_matches_async )*

From 5dbdc94be86a443bc7c12f0d974b0dfd10b67729 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Tue, 27 Jul 2021 14:35:50 +0300
Subject: [PATCH 64/72] Book corrections

---
 docs/book/content/tracing/index.md           | 87 +++++++++-----------
 juniper/src/lib.rs                           |  5 --
 juniper/src/tests/fixtures/tracing/schema.rs | 55 +++++++------
 juniper/src/util.rs                          | 22 -----
 4 files changed, 66 insertions(+), 103 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 0eb0b07a6..449149238 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -31,8 +31,8 @@ struct Foo {
 
 #[graphql_object]
 impl Foo {
-    // Value resolving is pretty straightforward so we can skip tracing.
-    #[tracing(no_trace)]
+    // Value resolving is pretty straight-forward so we can skip tracing.
+    #[graphql(tracing(ignore))]
     fn value(&self) -> i32 {
         self.value
     }
@@ -43,7 +43,7 @@ impl Foo {
     }
     
     // Here we'll record span and it will have field with name "self.value"
-    #[tracing(fields(self.value = self.value))]
+    #[instrument(fields(self.value = self.value))]
     fn square_value(&self) -> i32 {
         self.value * self.value
     }
@@ -99,9 +99,9 @@ async fn main() {
 ## Skipping field resolvers
 
 In certain scenarios you may want to skip tracing of some fields because it too
-simple and straightforward that tracing preparations of this resolver would actually
-take more time then execution. In this cases you can use `#[tracing(no_trace)]` to
-completely disable tracing of this field resolver.
+simple and straight-forward, that tracing preparations of this resolver would actually
+take more time then execution. In this cases you can use `tracing(ignore)` argument of
+`#[graphql]` attribute to completely disable tracing of this field resolver.
 
 ### Example
 
@@ -119,45 +119,44 @@ struct User {
 
 #[graphql_object(context = Context)]
 impl User {
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
     }
 
     async fn friends(&self, context: &Context) -> Vec<User> {
         // Some async query in which you're actually interested.
-        vec![]
+#       unimplemented!()
     }
 }
 ```
 
-Manually setting `#[tracing(no_traces)]` to avoid tracing of all, let's say for
+Manually setting `#[graphql(tracing(ignore))]` to avoid tracing of all, let's say for
 example synchronous field resolvers is rather inefficient when you have GraphQL
 object with too much fields. In this case you can use `tracing` argument on
 top-level `#[graphql_object]`, `#[graphql_interface]` or `#[graphql]` (when it
 used with `#[derive(GraphQLObject)]`) attributes to trace specific group of
 fields or not to trace at all. `tracing` argument can be used with one of the
-following arguments: `"sync"`, `"async"`, `"complex"` or `"skip-all"`.
- - Use `"sync"` to trace only synchronous part (struct fields and `fn`s).
- - Use `"async"` to trace only asynchronous part (`async fn`s) and
+following arguments: `sync`, `async`, `only` or `skip_all`.
+ - Use `sync` to trace only synchronous part (struct fields and `fn`s).
+ - Use `async` to trace only asynchronous part (`async fn`s) and
 subscriptions.
- - Use `"complex"` to trace only fields marked with `#[tracing(complex)]`
- - Use `"skip-all"` to skip tracing of all fields.
+ - Use `only` to trace only fields marked with `#[graphql(tracing(only))]`
+ - Use `skip_all` to skip tracing of all fields.
 
-**Note:** using of `trace = "sync"` with derived struct is no-op because all
+**Note:** using of `tracing(sync)` with derived struct is no-op because all
 resolvers within derived GraphQL object is considered to be synchronous, also
-because of this `trace = "async"` will result in no traces.
+because of this `tracing(async)` will result in no traces.
 
-In addition you can use `#[tracing(no_trace)]` with all variants above to
-exclude field from tracing even if it belongs to traced group.
+In addition you can use `#[graphql(tracing(ignore))]` with `skip` and `async`
+variants to exclude field from tracing even if it belongs to traced group.
 
 **Be careful when skipping trace as it can lead to bad structured span trees,
 disabling of tracing on one level won't disable tracing in it's child methods.**
 
 If resolving of certain field requires additional arguments (when used `fn`s or
 `async fn`s) they also will be included in resulted trace (except `self` and
-`Context`). You can use `skip` argument of `#[tracing]` attribute, to skip some
-arguments, similarly to the [`skip`] for `#[instrument]`
+`Context`).
 
 ```rust
 # extern crate juniper;
@@ -174,9 +173,8 @@ arguments, similarly to the [`skip`] for `#[instrument]`
 # struct Product {
 #     id: i32   
 # }
-
-struct Catalog;
-
+#
+# struct Catalog;
 #[graphql_object]
 impl Catalog {
     async fn products(filter: Filter, count: i32) -> Vec<Product> {
@@ -184,30 +182,15 @@ impl Catalog {
 # unimplemented!()
     }
 }
-
-struct User {
-    id: i32
-}
-
-#[graphql_object]
-impl User {
-    fn id(&self) -> i32 {
-        self.id
-    }
-
-    async fn friends(&self) -> Vec<i32> {
-        // async database query 
-# unimplemented!()
-    }
-}
 ```
 
-In case above both `filter` and `count` will be recorded in [`Span`] for
-`Catalog::products(...)`. All fields will be recorded using their `Debug`
-implementation, if your field doesn't implement `Debug` you should skip it
-with `#[tracing(skip(<fields to skip>))]` if you still want to record it but
-for some reason you don't want or can't use `Debug` trait, consider reintroducing
-this field with `fields(field_name = some_value)`.
+In example above both `filter` and `count` of `products` field will be recorded
+in produced [`Span`]. All fields will be recorded using their `fmt::Debug`
+implementation, if your field doesn't implement `fmt::Debug` you'll get compile
+time error. In this case ypu should either implement `fmt::Debug` or skip it
+using `#[instrument(skip(<fields to skip>))]` if you still want to record it but
+for some reason you don't want to implement `fmt::Debug` trait, consider reintroducing
+this field with `fields(field_name = some_value)` like shown bellow.
 
 
 ### Example
@@ -228,16 +211,22 @@ struct NonDebug {
 
 # #[graphql_object]
 # impl Query {
-#[tracing(skip(non_debug), fields(non_debug = non_debug.important_field.clone()))]
+// Note that you can use name of the skipped field as alias.
+#[instrument(skip(non_debug), fields(non_debug = non_debug.important_field.clone()))]
 fn my_query(&self, non_debug: NonDebug) -> i32 {
-    24
+    // Some query
+#    unimplemented!()
 }
 # }
 ```
 
-## `#[tracing]` attribute
+Custom fields generated this way are context aware and can use both `context` and `self`
+even if they're not implicitly passed to resolver. In case when resolver is `fn` with not
+only `self` and `context` arguments they're also available to interact with as shown above.
+
+## `#[instrument]` attribute
 
-In most aspects `#[tracing]` mimics behaviour of the `#[instrument]` attribute
+In most aspects it mimics behaviour of the original `#[instrument]` attribute
 from [tracing] crate and you could use it as a reference. With the only key
 deference you should understand, it applied implicitly to all resolvers if the
 `tracing` feature is enabled.
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 17013d569..47c30522c 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -148,11 +148,6 @@ pub mod tests;
 #[cfg(test)]
 mod executor_tests;
 
-// Needs to be public because macros use it.
-pub use crate::util::to_camel_case;
-#[cfg(feature = "tracing")]
-pub use crate::util::tracing::InstrumentInternal;
-
 use crate::{
     executor::{execute_validated_query, get_operation},
     introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index e0b5d3e23..f04473131 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -41,6 +41,7 @@ impl Database {
 pub struct Query;
 
 #[graphql_object(context = Database)]
+#[graphql(tracing(...))]
 impl Query {
     /// Simple sync query with no arguments.
     fn foo() -> Foo {
@@ -86,47 +87,47 @@ impl Query {
         ]
     }
 
-    /// Returns GraphQL object marked with `trace = "async"`.
+    /// Returns GraphQL object marked with `tracing(async)`.
     async fn trace_async() -> TraceAsync {
         TraceAsync
     }
 
-    /// Returns derived GraphQL object marked with `trace = "async"`.
+    /// Returns derived GraphQL object marked with `tracing(async)`.
     async fn derived_async() -> AsyncDerived {
         AsyncDerived::default()
     }
 
-    /// Returns GraphQL object marked with `trace = "sync"`.
+    /// Returns GraphQL object marked with `tracing(sync)`.
     fn trace_sync() -> TraceSync {
         TraceSync
     }
 
-    /// Returns derived GraphQL object marked with `trace = "sync"`.
+    /// Returns derived GraphQL object marked with `tracing(sync)`.
     fn derived_sync() -> SyncDerived {
         SyncDerived::default()
     }
 
-    /// Returns GraphQL object marked with `trace = "skip-all"`.
+    /// Returns GraphQL object marked with `tracing(skip_all)`.
     fn skip_all() -> SkipAll {
         SkipAll
     }
 
-    /// Returns derived GraphQL object marked with `trace = "skip-all"`.
+    /// Returns derived GraphQL object marked with `tracing(skip_all)`.
     fn skip_all_derived() -> SkipAllDerived {
         SkipAllDerived::default()
     }
 
-    /// Returns GraphQL object marked with `trace = "complex"` in sync manner.
+    /// Returns GraphQL object marked with `tracing(complex)` in sync manner.
     fn complex_sync() -> Complex {
         Complex
     }
 
-    /// Returns GraphQL object marked with `trace = "complex"` in async manner.
+    /// Returns GraphQL object marked with `tracing(complex)` in async manner.
     async fn complex_async() -> Complex {
         Complex
     }
 
-    /// Returns derived GraphQL object marked with `trace = "complex"`.
+    /// Returns derived GraphQL object marked with `tracing(complex)`.
     fn complex_derived() -> DerivedComplex {
         DerivedComplex {
             complex: false,
@@ -140,22 +141,22 @@ impl Query {
         InterfacedSimpleValue::TraceSync(TraceSync)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "sync"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(sync)`.
     fn erased_sync() -> InterfacedSyncValue {
         InterfacedSyncValue::TraceSync(TraceSync)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "async"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(async)`.
     fn erased_async() -> InterfacedAsyncValue {
         InterfacedAsyncValue::TraceAsync(TraceAsync)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "skip-all"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(skip_all)`.
     fn erased_skip_all() -> InterfacedSkipAllValue {
         InterfacedSkipAllValue::SkipAll(SkipAll)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `trace = "complex"`.
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(complex)`.
     fn erased_complex() -> InterfacedComplexValue {
         InterfacedComplexValue::Complex(Complex)
     }
@@ -200,13 +201,13 @@ impl Foo {
         self.id
     }
 
-    /// Sync field marked with `no_trace`.
+    /// Sync field marked with `tracing(ignore)`.
     #[graphql(tracing(ignore))]
     fn non_traced(&self) -> &str {
         "None can trace this"
     }
 
-    /// Async field marked with `no_trace`.
+    /// Async field marked with `tracing(ignore)`.
     #[graphql(tracing(ignore))]
     async fn async_non_traced(&self) -> &str {
         "None can trace this"
@@ -298,7 +299,7 @@ pub struct DerivedFoo {
     #[instrument(fields(self.id = self.id, custom_fields = "work"))]
     id: i32,
 
-    /// Field marked with `no_trace` within derived [`GraphQLObject`].
+    /// Field marked with `tracing(ignore)` within derived [`GraphQLObject`].
     #[graphql(tracing(ignore))]
     non_traced: String,
 
@@ -331,13 +332,13 @@ pub trait FooBar {
     /// Simple async field.
     async fn is_bar(&self) -> bool;
 
-    /// Interface field marked with `no_trace`.
+    /// Interface field marked with `tracing(ignore)`.
     #[graphql(tracing(ignore))]
     fn non_traced(&self) -> bool {
         true
     }
 
-    /// Async interface field marked with `no_trace`.
+    /// Async interface field marked with `tracing(ignore)`.
     #[graphql(tracing(ignore))]
     async fn async_non_traced(&self) -> bool {
         true
@@ -367,20 +368,20 @@ pub trait FooBar {
         id + skipped + default + overwritten
     }
 
-    /// Field with overwritten `target` of span.
+    /// Field with its `target` being overwritten.
     #[instrument(target = "my_target")]
     fn target(&self) -> i32 {
         1
     }
 
-    /// Field with overwritten `level` of span.
+    /// Field with its `level` being overwritten.
     #[instrument(level = "warn")]
     fn level(&self) -> i32 {
         2
     }
 }
 
-/// GraphQL object marked with `trace = "skip-sync"`.
+/// GraphQL object marked with `tracing(sync)`.
 pub struct TraceSync;
 
 #[graphql_object(
@@ -400,7 +401,7 @@ impl TraceSync {
 build_impl!(TraceSync, InterfacedSimple);
 build_impl!(TraceSync, InterfacedSync);
 
-/// Derived GraphQL object marked with `trace = "sync"`.
+/// Derived GraphQL object marked with `tracing(sync)`.
 #[derive(Default, GraphQLObject)]
 #[graphql(tracing(sync))]
 pub struct SyncDerived {
@@ -408,7 +409,7 @@ pub struct SyncDerived {
     sync: i32,
 }
 
-/// GraphQL object marked with `trace = "async"`.
+/// GraphQL object marked with `tracing(async)`.
 pub struct TraceAsync;
 
 #[graphql_object(
@@ -435,7 +436,7 @@ pub struct AsyncDerived {
     sync: i32,
 }
 
-/// GraphQL object marked with `trace = "skip-all"`.
+/// GraphQL object marked with `tracing(skip_all)`.
 pub struct SkipAll;
 
 #[graphql_object(
@@ -454,7 +455,7 @@ impl SkipAll {
 
 build_impl!(SkipAll, InterfacedSkipAll);
 
-/// Derived GraphQL object marked with `trace = "skip-all"`.
+/// Derived GraphQL object marked with `tracing(skip_all)`.
 #[derive(Default, GraphQLObject)]
 #[graphql(tracing(skip_all))]
 pub struct SkipAllDerived {
@@ -462,7 +463,7 @@ pub struct SkipAllDerived {
     sync: i32,
 }
 
-/// GraphQL object marked with `trace = "complex"`.
+/// GraphQL object marked with `tracing(only)`.
 pub struct Complex;
 
 #[graphql_object(
@@ -487,7 +488,7 @@ impl Complex {
 
 build_impl!(Complex, InterfacedComplex);
 
-/// Derived GraphQL object marked with `trace = "complex"`.
+/// Derived GraphQL object marked with `tracing(only)`.
 #[derive(GraphQLObject)]
 #[graphql(tracing(only))]
 pub struct DerivedComplex {
diff --git a/juniper/src/util.rs b/juniper/src/util.rs
index e6d421872..08f51493d 100644
--- a/juniper/src/util.rs
+++ b/juniper/src/util.rs
@@ -51,25 +51,3 @@ fn test_to_camel_case() {
     assert_eq!(&to_camel_case("a")[..], "a");
     assert_eq!(&to_camel_case("")[..], "");
 }
-
-#[cfg(feature = "tracing")]
-#[doc(hidden)]
-pub mod tracing {
-    use crate::{
-        tracing::Span,
-        tracing_futures::{Instrument, Instrumented},
-    };
-
-    /// Helper trait required to pass sanity tests when [`Instrument`] is
-    /// imported already.
-    pub trait InstrumentInternal {
-        fn __instrument(self, span: Span) -> Instrumented<Self>
-        where
-            Self: Sized,
-        {
-            self.instrument(span)
-        }
-    }
-
-    impl<T> InstrumentInternal for T where T: Instrument {}
-}

From c256a2e98a299b8a51ebf4a6db777aa883607a25 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Thu, 12 Aug 2021 14:16:59 +0300
Subject: [PATCH 65/72] Restore tracing codegen

---
 docs/book/content/tracing/index.md            |   2 +-
 juniper/src/tests/fixtures/tracing/mod.rs     |   1 +
 juniper/src/tests/fixtures/tracing/schema.rs  |  14 ++-
 juniper_codegen/src/common/field/arg.rs       |  45 +++++---
 juniper_codegen/src/common/field/mod.rs       | 103 ++++++++++++++----
 juniper_codegen/src/derive_enum.rs            |   2 +-
 juniper_codegen/src/derive_input_object.rs    |   2 +-
 juniper_codegen/src/graphql_interface/attr.rs |   4 +-
 juniper_codegen/src/graphql_interface/mod.rs  |  16 ++-
 .../src/graphql_interface/tracing.rs          |  31 +++---
 juniper_codegen/src/graphql_object/attr.rs    |  10 ++
 juniper_codegen/src/graphql_object/derive.rs  |  14 ++-
 juniper_codegen/src/graphql_object/mod.rs     |  50 ++++++++-
 juniper_codegen/src/graphql_object/tracing.rs |  18 +++
 .../src/graphql_subscription/mod.rs           |   6 +-
 juniper_codegen/src/graphql_union/mod.rs      |   7 +-
 juniper_codegen/src/lib.rs                    |   2 +-
 juniper_codegen/src/tracing.rs                |  57 ++++++++--
 juniper_codegen/src/util/mod.rs               |  12 +-
 19 files changed, 310 insertions(+), 86 deletions(-)
 create mode 100644 juniper_codegen/src/graphql_object/tracing.rs

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 449149238..cbf21fe25 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -226,7 +226,7 @@ only `self` and `context` arguments they're also available to interact with as s
 
 ## `#[instrument]` attribute
 
-In most aspects it mimics behaviour of the original `#[instrument]` attribute
+In most aspects it mimics behavior of the original `#[instrument]` attribute
 from [tracing] crate and you could use it as a reference. With the only key
 deference you should understand, it applied implicitly to all resolvers if the
 `tracing` feature is enabled.
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 73ad96bbb..55861abdc 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -172,6 +172,7 @@ impl Subscriber for TestSubscriber {
 }
 
 /// Wrapper representing span tree received from [`TestSubscriber`].
+#[derive(Debug)]
 pub struct SubscriberAssert {
     name_to_span: HashMap<span::Id, String>,
     events: Vec<SubscriberEvent>,
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index f04473131..b15846f0e 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -3,7 +3,7 @@
 
 use std::collections::HashMap;
 
-use futures::stream::{self, BoxStream};
+use futures::stream::{self, BoxStream, StreamExt as _};
 
 use crate::{
     graphql_interface, graphql_object, graphql_subscription, tracing, Context, GraphQLObject,
@@ -41,7 +41,6 @@ impl Database {
 pub struct Query;
 
 #[graphql_object(context = Database)]
-#[graphql(tracing(...))]
 impl Query {
     /// Simple sync query with no arguments.
     fn foo() -> Foo {
@@ -263,9 +262,16 @@ impl Bar {
     }
 
     /// Field with default arguments.
-    #[graphql(arguments(this(default = 42), another(default = 0), skipped(default = 1),))]
     #[instrument(skip(skipped))]
-    fn default_arg(&self, this: i32, another: i32, skipped: i32) -> i32 {
+    fn default_arg(
+        &self,
+        #[graphql(default = 42)]
+        this: i32,
+        #[graphql(default = 0)]
+        another: i32,
+        #[graphql(default = 1)]
+        skipped: i32
+    ) -> i32 {
         this + another + skipped
     }
 
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index 0c6d16a94..feabc0e76 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -237,6 +237,8 @@ pub(crate) struct OnField {
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments
     pub(crate) name: String,
 
+    pub(crate) raw_name: syn::Ident,
+
     /// [Description][2] of this [GraphQL field argument][1] to put into GraphQL
     /// schema.
     ///
@@ -348,26 +350,39 @@ impl OnMethod {
     ///
     /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
     #[must_use]
-    pub(crate) fn method_resolve_field_tokens(&self, scalar: &scalar::Type) -> TokenStream {
+    pub(crate) fn method_resolve_field_tokens(&self) -> TokenStream {
+        match self {
+            Self::Regular(arg) => {
+                let name = &arg.raw_name;
+                quote! { #name }
+            }
+
+            Self::Context(_) => quote! {
+                ::juniper::FromContext::from(executor.context())
+            },
+
+            Self::Executor => quote! { &executor },
+        }
+    }
+
+    #[must_use]
+    pub(crate) fn method_resolve_arg_getter_tokens(&self, scalar: &scalar::Type) -> TokenStream {
+
         match self {
             Self::Regular(arg) => {
-                let (name, ty) = (&arg.name, &arg.ty);
+                let (name, raw_name, ty) = (&arg.name, &arg.raw_name, &arg.ty);
                 let err_text = format!(
                     "Internal error: missing argument `{}` - validation must have failed",
                     &name,
                 );
                 quote! {
-                    args.get::<#ty>(#name)
+                    let #raw_name = args.get::<#ty>(#name)
                         .or_else(::juniper::FromInputValue::<#scalar>::from_implicit_null)
-                        .expect(#err_text)
+                            .expect(#err_text);
+
                 }
             }
-
-            Self::Context(_) => quote! {
-                ::juniper::FromContext::from(executor.context())
-            },
-
-            Self::Executor => quote! { &executor },
+            Self::Context(_) | Self::Executor => quote!()
         }
     }
 
@@ -415,10 +430,13 @@ impl OnMethod {
             }
         }
 
-        let name = if let Some(name) = attr.name.as_ref() {
-            name.as_ref().value()
+        let (name, raw_name) = if let Some(name) = attr.name.as_ref() {
+            let field_name = name.as_ref().value();
+            let raw_name = syn::Ident::new(&field_name, name.span());
+            (field_name, raw_name)
         } else if let syn::Pat::Ident(name) = &*argument.pat {
-            renaming.apply(&name.ident.unraw().to_string())
+            let raw_name = name.ident.clone();
+            (renaming.apply(&raw_name.unraw().to_string()), raw_name)
         } else {
             scope
                 .custom(
@@ -444,6 +462,7 @@ impl OnMethod {
 
         Some(Self::Regular(OnField {
             name,
+            raw_name,
             ty: argument.ty.as_ref().clone(),
             description: attr.description.as_ref().map(|d| d.as_ref().value()),
             default: attr.default.as_ref().map(|v| v.as_ref().clone()),
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index a627e444b..8d6c90476 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -25,6 +25,8 @@ use crate::{
     },
     util::{filter_attrs, get_deprecated, get_doc_comment, span_container::SpanContainer},
 };
+#[cfg(feature = "tracing")]
+use crate::tracing;
 
 pub(crate) use self::arg::OnMethod as MethodArgument;
 
@@ -79,6 +81,9 @@ pub(crate) struct Attr {
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     /// [2]: https://spec.graphql.org/June2018/#sec-Objects
     pub(crate) downcast: Option<SpanContainer<syn::Ident>>,
+
+    #[cfg(feature = "tracing")]
+    pub(crate) tracing_behavior: Option<SpanContainer<tracing::FieldBehavior>>,
 }
 
 impl Parse for Attr {
@@ -123,6 +128,15 @@ impl Parse for Attr {
                     .downcast
                     .replace(SpanContainer::new(ident.span(), None, ident.clone()))
                     .none_or_else(|_| err::dup_arg(&ident))?,
+                #[cfg(feature = "tracing")]
+                "tracing" => {
+                    let content;
+                    syn::parenthesized!(content in input);
+                    let behavior = content.parse_any_ident()?;
+                    out.tracing_behavior
+                        .replace(SpanContainer::new(ident.span(), Some(behavior.span()), tracing::FieldBehavior::from_ident(&behavior)?))
+                        .none_or_else(|_| err::dup_arg(&ident))?;
+                },
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -143,6 +157,7 @@ impl Attr {
             deprecated: try_merge_opt!(deprecated: self, another),
             ignore: try_merge_opt!(ignore: self, another),
             downcast: try_merge_opt!(downcast: self, another),
+            tracing_behavior: try_merge_opt!(tracing_behavior: self, another),
         })
     }
 
@@ -256,6 +271,12 @@ pub(crate) struct Definition {
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     pub(crate) is_async: bool,
+
+    #[cfg(feature = "tracing")]
+    pub(crate) instrument: Option<tracing::Attr>,
+
+    #[cfg(feature = "tracing")]
+    pub(crate) tracing: Option<tracing::FieldBehavior>,
 }
 
 impl Definition {
@@ -396,6 +417,8 @@ impl Definition {
         &self,
         scalar: &scalar::Type,
         trait_ty: Option<&syn::Type>,
+        #[cfg(feature = "tracing")]
+        traced_ty: &impl tracing::TracedType,
     ) -> Option<TokenStream> {
         if self.is_async {
             return None;
@@ -403,32 +426,43 @@ impl Definition {
 
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
-        let res = if self.is_method() {
-            let args = self
+        let (res, getters) = if self.is_method() {
+            let (args, getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| arg.method_resolve_field_tokens(scalar));
+                .map(|arg| (
+                    arg.method_resolve_field_tokens(),
+                    arg.method_resolve_arg_getter_tokens(scalar),
+                ))
+                .unzip();
 
             let rcv = self.has_receiver.then(|| {
                 quote! { self, }
             });
 
-            if trait_ty.is_some() {
+            let res = if trait_ty.is_some() {
                 quote! { <Self as #trait_ty>::#ident(#rcv #( #args ),*) }
             } else {
                 quote! { Self::#ident(#rcv #( #args ),*) }
-            }
+            };
+            (res, quote!(#( #getters )*))
         } else {
             ty = parse_quote! { _ };
-            quote! { &self.#ident }
+            (quote! { &self.#ident }, quote!())
         };
 
         let resolving_code = gen::sync_resolving_code();
+        let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
+        let trace_sync = if_tracing_enabled!(tracing::sync_tokens(traced_ty, self));
 
         Some(quote! {
             #name => {
+                #getters
+                #span
+                #trace_sync
+
                 let res: #ty = #res;
                 #resolving_code
             }
@@ -446,38 +480,51 @@ impl Definition {
         &self,
         scalar: &scalar::Type,
         trait_ty: Option<&syn::Type>,
+        #[cfg(feature = "tracing")]
+        traced_ty: &impl tracing::TracedType,
     ) -> TokenStream {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
-        let mut fut = if self.is_method() {
-            let args = self
+        let (mut fut, fields) = if self.is_method() {
+            let (args, getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| arg.method_resolve_field_tokens(scalar));
+                .map(|arg| (
+                    arg.method_resolve_field_tokens(),
+                    arg.method_resolve_arg_getter_tokens(scalar),
+                ))
+                .unzip();
 
             let rcv = self.has_receiver.then(|| {
                 quote! { self, }
             });
 
-            if trait_ty.is_some() {
+            let fut = if trait_ty.is_some() {
                 quote! { <Self as #trait_ty>::#ident(#rcv #( #args ),*) }
             } else {
                 quote! { Self::#ident(#rcv #( #args ),*) }
-            }
+            };
+            (fut, quote!( #( #getters )*))
         } else {
             ty = parse_quote! { _ };
-            quote! { &self.#ident }
+            (quote! { &self.#ident }, quote!())
         };
         if !self.is_async {
             fut = quote! { ::juniper::futures::future::ready(#fut) };
         }
 
-        let resolving_code = gen::async_resolving_code(Some(&ty));
+        let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
+        let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
+
+        let resolving_code = gen::async_resolving_code(Some(&ty), trace_async);
 
         quote! {
             #name => {
+                #fields
+                #span
+
                 let fut = #fut;
                 #resolving_code
             }
@@ -495,37 +542,52 @@ impl Definition {
     pub(crate) fn method_resolve_field_into_stream_tokens(
         &self,
         scalar: &scalar::Type,
+        #[cfg(feature = "tracing")]
+        traced_ty: &impl tracing::TracedType,
     ) -> TokenStream {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
-        let mut fut = if self.is_method() {
-            let args = self
+        let (mut fut, args) = if self.is_method() {
+            let (args, getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| arg.method_resolve_field_tokens(scalar));
+                .map(|arg| (
+                    arg.method_resolve_field_tokens(),
+                    arg.method_resolve_arg_getter_tokens(scalar),
+                ))
+                .unzip();
 
             let rcv = self.has_receiver.then(|| {
                 quote! { self, }
             });
 
-            quote! { Self::#ident(#rcv #( #args ),*) }
+            (
+                quote! { Self::#ident(#rcv #( #args ),*) },
+                quote! { #( #getters )* }
+            )
         } else {
             ty = parse_quote! { _ };
-            quote! { &self.#ident }
+            (quote! { &self.#ident }, quote!())
         };
         if !self.is_async {
             fut = quote! { ::juniper::futures::future::ready(#fut) };
         }
 
+        let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
+        let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
+
         quote! {
             #name => {
+                #args
+                #span
+
                 ::juniper::futures::FutureExt::boxed(async move {
                     let res: #ty = #fut.await;
                     let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
                     let executor = executor.as_owned_executor();
-                    let stream = ::juniper::futures::StreamExt::then(res, move |res| {
+                    let f = ::juniper::futures::StreamExt::then(res, move |res| {
                         let executor = executor.clone();
                         let res2: ::juniper::FieldResult<_, #scalar> =
                             ::juniper::IntoResolvable::into(res, executor.context());
@@ -543,9 +605,10 @@ impl Definition {
                             }
                         }
                     });
+                    #trace_async
                     Ok(::juniper::Value::Scalar::<
                         ::juniper::ValuesStream::<#scalar>
-                    >(::juniper::futures::StreamExt::boxed(stream)))
+                    >(::juniper::futures::StreamExt::boxed(f)))
                 })
             }
         }
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index 8fee73aad..615e92eed 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -102,7 +102,7 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
 
                 // Enums cannot be traced.
                 #[cfg(feature = "tracing")]
-                tracing_behaviour: None,
+                tracing_behavior: None,
                 #[cfg(feature = "tracing")]
                 instrument_attr: None,
             })
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index 3d4149c0e..6e06833c2 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -98,7 +98,7 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
 
                 // Tracing of GraphQL Input Objects not supported
                 #[cfg(feature = "tracing")]
-                tracing_behaviour: None,
+                tracing_behavior: None,
                 #[cfg(feature = "tracing")]
                 instrument_attr: None,
             })
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index 089c0a954..cd9d09740 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -203,7 +203,7 @@ fn expand_on_trait(
         implementers,
 
         #[cfg(feature = "tracing")]
-        tracing_rule: meta.tracing_rule.map(|t| *t.inner()),
+        tracing_rule: attr.tracing_rule.map(|t| t.into_inner()),
     };
 
     // Attach the `juniper::AsDynGraphQLValue` on top of the trait if dynamic dispatch is used.
@@ -480,7 +480,7 @@ impl TraitMethod {
             #[cfg(feature = "tracing")]
             instrument: tracing::Attr::from_trait_method(method),
             #[cfg(feature = "tracing")]
-            tracing: meta.tracing_behaviour.map(|t| t.into_inner()),
+            tracing: attr.tracing_behavior.map(|t| t.into_inner()),
         })
     }
 }
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index ead8ae43c..8b897e4ac 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -261,7 +261,7 @@ impl Parse for TraitAttr {
                     let tracing = content.parse_any_ident()?;
                     let tracing_rule = tracing::Rule::from_str(tracing.to_string().as_str());
                     match tracing_rule {
-                        Ok(rule) => output
+                        Ok(rule) => out
                             .tracing_rule
                             .replace(SpanContainer::new(span, Some(tracing.span()), rule))
                             .none_or_else(|_| err::dup_arg(span))?,
@@ -626,7 +626,12 @@ impl Definition {
         let fields_resolvers = self
             .fields
             .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(scalar, Some(&trait_ty)));
+            .filter_map(|f| f.method_resolve_field_tokens(
+                scalar,
+                Some(&trait_ty),
+                #[cfg(feature = "tracing")]
+                self,
+            ));
         let async_fields_panic = {
             let names = self
                 .fields
@@ -716,7 +721,12 @@ impl Definition {
         let fields_resolvers = self
             .fields
             .iter()
-            .map(|f| f.method_resolve_field_async_tokens(scalar, Some(&trait_ty)));
+            .map(|f| f.method_resolve_field_async_tokens(
+                scalar,
+                Some(&trait_ty),
+                #[cfg(feature = "tracing")]
+                self,
+            ));
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         let custom_downcasts = self
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
index 1221528f5..957cce64b 100644
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ b/juniper_codegen/src/graphql_interface/tracing.rs
@@ -1,11 +1,13 @@
-pub use crate::tracing::{
-    async_tokens, span_tokens, sync_tokens, Attr, FieldBehaviour, Rule, TracedArgument,
-    TracedField, TracedType,
+pub use crate::{
+    tracing::{
+        async_tokens, span_tokens, sync_tokens, Attr, FieldBehavior, Rule, TracedArgument,
+        TracedField, TracedType,
+    }
 };
 
-use super::{Definition, Field, FieldArgument};
+use super::{Definition as InterfaceDefinition, field};
 
-impl TracedType for Definition {
+impl TracedType for InterfaceDefinition {
     fn tracing_rule(&self) -> Rule {
         self.tracing_rule.unwrap_or(Rule::All)
     }
@@ -19,15 +21,15 @@ impl TracedType for Definition {
     }
 }
 
-impl TracedField for Field {
-    type Arg = FieldArgument;
+impl TracedField for field::Definition {
+    type Arg = field::arg::OnField;
 
     fn instrument(&self) -> Option<&Attr> {
         self.instrument.as_ref()
     }
 
-    fn tracing_behaviour(&self) -> FieldBehaviour {
-        self.tracing.unwrap_or(FieldBehaviour::Default)
+    fn tracing_behavior(&self) -> FieldBehavior {
+        self.tracing.unwrap_or(FieldBehavior::Default)
     }
 
     fn is_async(&self) -> bool {
@@ -40,13 +42,16 @@ impl TracedField for Field {
 
     fn args(&self) -> Vec<&Self::Arg> {
         self.arguments
-            .iter()
-            .filter_map(|arg| arg.as_regular())
-            .collect()
+            .as_ref()
+            .map_or_else(
+                || vec![],
+                |args| args.iter()
+                    .filter_map(|arg| arg.as_regular())
+                    .collect())
     }
 }
 
-impl TracedArgument for FieldArgument {
+impl TracedArgument for field::arg::OnField {
     fn ty(&self) -> &syn::Type {
         &self.ty
     }
diff --git a/juniper_codegen/src/graphql_object/attr.rs b/juniper_codegen/src/graphql_object/attr.rs
index 34a70c211..51315c33d 100644
--- a/juniper_codegen/src/graphql_object/attr.rs
+++ b/juniper_codegen/src/graphql_object/attr.rs
@@ -15,6 +15,8 @@ use crate::{
     result::GraphQLScope,
     util::{path_eq_single, span_container::SpanContainer, RenameRule},
 };
+#[cfg(feature = "tracing")]
+use crate::tracing;
 
 use super::{Attr, Definition, Query};
 
@@ -128,6 +130,9 @@ where
             .map(|ty| ty.as_ref().clone())
             .collect(),
         _operation: PhantomData,
+
+        #[cfg(feature = "tracing")]
+        tracing: attr.tracing_rule.map(|t| t.into_inner())
     };
 
     Ok(quote! {
@@ -231,6 +236,11 @@ fn parse_field(
         arguments: Some(arguments),
         has_receiver: method.sig.receiver().is_some(),
         is_async: method.sig.asyncness.is_some(),
+
+        #[cfg(feature = "tracing")]
+        instrument: tracing::Attr::from_method(method),
+        #[cfg(feature = "tracing")]
+        tracing: attr.tracing_behavior.map(|t| t.into_inner()),
     })
 }
 
diff --git a/juniper_codegen/src/graphql_object/derive.rs b/juniper_codegen/src/graphql_object/derive.rs
index 7cbe961b8..5fa07ecd8 100644
--- a/juniper_codegen/src/graphql_object/derive.rs
+++ b/juniper_codegen/src/graphql_object/derive.rs
@@ -12,6 +12,8 @@ use crate::{
     result::GraphQLScope,
     util::{span_container::SpanContainer, RenameRule},
 };
+#[cfg(feature = "tracing")]
+use crate::tracing;
 
 use super::{Attr, Definition, Query};
 
@@ -65,8 +67,8 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
         .unwrap_or(RenameRule::CamelCase);
 
     let mut fields = vec![];
-    if let syn::Data::Struct(data) = &ast.data {
-        if let syn::Fields::Named(fs) = &data.fields {
+    if let syn::Data::Struct(data) = ast.data {
+        if let syn::Fields::Named(fs) = data.fields {
             fields = fs
                 .named
                 .iter()
@@ -105,6 +107,9 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
             .map(|ty| ty.as_ref().clone())
             .collect(),
         _operation: PhantomData,
+
+        #[cfg(feature = "tracing")]
+        tracing: attr.tracing_rule.map(|t| t.into_inner()),
     })
 }
 
@@ -156,5 +161,10 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option<field::Defin
         arguments: None,
         has_receiver: false,
         is_async: false,
+
+        #[cfg(feature = "tracing")]
+        instrument: tracing::Attr::from_field(field),
+        #[cfg(feature = "tracing")]
+        tracing: attr.tracing_behavior.map(|t| t.into_inner()),
     })
 }
diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs
index 267fde795..0a2d256a7 100644
--- a/juniper_codegen/src/graphql_object/mod.rs
+++ b/juniper_codegen/src/graphql_object/mod.rs
@@ -5,11 +5,16 @@
 pub mod attr;
 pub mod derive;
 
-use std::{any::TypeId, collections::HashSet, convert::TryInto as _, marker::PhantomData};
+#[cfg(feature = "tracing")]
+mod tracing;
 
+use std::{any::TypeId, collections::HashSet, convert::TryInto as _, marker::PhantomData, str::FromStr as _};
+
+use proc_macro_error::abort;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
 use syn::{
+    ext::IdentExt as _,
     parse::{Parse, ParseStream},
     parse_quote,
     spanned::Spanned as _,
@@ -27,7 +32,6 @@ use crate::{
     },
     util::{filter_attrs, get_doc_comment, span_container::SpanContainer, RenameRule},
 };
-use syn::ext::IdentExt;
 
 /// Available arguments behind `#[graphql]` (or `#[graphql_object]`) attribute
 /// when generating code for [GraphQL object][1] type.
@@ -91,6 +95,9 @@ pub(crate) struct Attr {
     /// Indicator whether the generated code is intended to be used only inside
     /// the [`juniper`] library.
     pub(crate) is_internal: bool,
+
+    #[cfg(feature = "tracing")]
+    pub(crate) tracing_rule: Option<SpanContainer<tracing::Rule>>
 }
 
 impl Parse for Attr {
@@ -161,6 +168,27 @@ impl Parse for Attr {
                 "internal" => {
                     out.is_internal = true;
                 }
+                #[cfg(feature = "tracing")]
+                "tracing" => {
+                    let content;
+                    syn::parenthesized!(content in input);
+                    let tracing = content.parse_any_ident()?;
+                    let tracing_rule = tracing::Rule::from_str(tracing.to_string().as_str());
+                    match tracing_rule {
+                        Ok(rule) => out
+                            .tracing_rule
+                            .replace(SpanContainer::new(ident.span(), Some(tracing.span()), rule))
+                            .none_or_else(|_| err::dup_arg(ident.span()))?,
+                        Err(_) => abort!(syn::Error::new(
+                            tracing.span(),
+                            format!(
+                                "Unknown tracing rule: {}, \
+                                 known values: sync, async, skip-all and complex",
+                                tracing,
+                            )
+                        )),
+                    }
+                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -183,6 +211,7 @@ impl Attr {
             interfaces: try_merge_hashset!(interfaces: self, another => span_joined),
             rename_fields: try_merge_opt!(rename_fields: self, another),
             is_internal: self.is_internal || another.is_internal,
+            tracing_rule: try_merge_opt!(tracing_rule: self, another),
         })
     }
 
@@ -265,6 +294,9 @@ pub(crate) struct Definition<Operation: ?Sized> {
     /// [2]: https://spec.graphql.org/June2018/#sec-Query
     /// [3]: https://spec.graphql.org/June2018/#sec-Subscription
     pub(crate) _operation: PhantomData<Box<Operation>>,
+
+    #[cfg(feature = "tracing")]
+    pub(crate) tracing: Option<tracing::Rule>,
 }
 
 impl<Operation: ?Sized + 'static> Definition<Operation> {
@@ -492,7 +524,12 @@ impl Definition<Query> {
         let fields_resolvers = self
             .fields
             .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(scalar, None));
+            .filter_map(|f| f.method_resolve_field_tokens(
+                scalar,
+                None,
+                #[cfg(feature = "tracing")]
+                self
+            ));
         let async_fields_panic = {
             let names = self
                 .fields
@@ -557,7 +594,12 @@ impl Definition<Query> {
         let fields_resolvers = self
             .fields
             .iter()
-            .map(|f| f.method_resolve_field_async_tokens(scalar, None));
+            .map(|f| f.method_resolve_field_async_tokens(
+                scalar,
+                None,
+                #[cfg(feature = "tracing")]
+                self,
+            ));
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         quote! {
diff --git a/juniper_codegen/src/graphql_object/tracing.rs b/juniper_codegen/src/graphql_object/tracing.rs
new file mode 100644
index 000000000..a773a3174
--- /dev/null
+++ b/juniper_codegen/src/graphql_object/tracing.rs
@@ -0,0 +1,18 @@
+pub use crate::tracing::*;
+
+use super::Definition;
+use syn::Type;
+
+impl<T: ?Sized> TracedType for Definition<T> {
+    fn tracing_rule(&self) -> Rule {
+        self.tracing.unwrap_or(Rule::All)
+    }
+
+    fn name(&self) -> &str {
+        &self.name
+    }
+
+    fn scalar(&self) -> Option<Type> {
+        Some(self.scalar.ty())
+    }
+}
diff --git a/juniper_codegen/src/graphql_subscription/mod.rs b/juniper_codegen/src/graphql_subscription/mod.rs
index 8f6b0e06f..1fd01c1c9 100644
--- a/juniper_codegen/src/graphql_subscription/mod.rs
+++ b/juniper_codegen/src/graphql_subscription/mod.rs
@@ -99,7 +99,11 @@ impl Definition<Subscription> {
         let fields_resolvers = self
             .fields
             .iter()
-            .map(|f| f.method_resolve_field_into_stream_tokens(scalar));
+            .map(|f| f.method_resolve_field_into_stream_tokens(
+                scalar,
+                #[cfg(feature = "tracing")]
+                self,
+            ));
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         quote! {
diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs
index c882c24ba..b34e6056c 100644
--- a/juniper_codegen/src/graphql_union/mod.rs
+++ b/juniper_codegen/src/graphql_union/mod.rs
@@ -691,12 +691,15 @@ impl VariantDefinition {
     fn method_resolve_into_type_async_tokens(&self, scalar: &scalar::Type) -> TokenStream {
         let ty = &self.ty;
         let expr = &self.resolver_code;
-        let resolving_code = gen::async_resolving_code(None);
+        let resolving_code = gen::async_resolving_code(None, quote!());
 
         quote! {
             if type_name == <#ty as ::juniper::GraphQLType<#scalar>>::name(info).unwrap() {
                 let fut = ::juniper::futures::future::ready({ #expr });
-                return #resolving_code;
+                let f = {
+                    #resolving_code
+                };
+                return f
             }
         }
     }
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index c04420553..d45b34ace 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -817,7 +817,7 @@ pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
 /// [`ScalarValue`]: juniper::ScalarValue
 /// [1]: https://spec.graphql.org/June2018/#sec-Objects
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLObject, attributes(graphql))]
+#[proc_macro_derive(GraphQLObject, attributes(graphql, instrument))]
 pub fn derive_object(body: TokenStream) -> TokenStream {
     self::graphql_object::derive::expand(body.into())
         .unwrap_or_abort()
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index e76d92c26..6f9f41f63 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -50,6 +50,39 @@ impl Attr {
             Err(e) => abort!(e),
         }
     }
+
+    pub fn from_method(method: &mut syn::ImplItemMethod) -> Option<Self> {
+        let attr = method
+            .attrs
+            .iter()
+            .find(|attr| attr.path.is_ident(&ATTR_NAME))
+            .map(|attr| attr.parse_args())
+            .transpose();
+
+        method.attrs = mem::take(&mut method.attrs)
+            .into_iter()
+            .filter(|attr| !attr.path.is_ident(&ATTR_NAME))
+            .collect();
+
+        match attr {
+            Ok(attr) => attr,
+            Err(e) => abort!(e),
+        }
+    }
+
+    pub fn from_field(field: &syn::Field) -> Option<Self> {
+        let attr = field
+            .attrs
+            .iter()
+            .find(|attr| attr.path.is_ident(&ATTR_NAME))
+            .map(|attr| attr.parse_args())
+            .transpose();
+
+        match attr {
+            Ok(attr) => attr,
+            Err(e) => abort!(e),
+        }
+    }
 }
 
 impl Parse for Attr {
@@ -143,8 +176,8 @@ impl FromStr for Rule {
 /// Marker on field which used together with [`Rule`] to decide whether this
 /// field should be traced.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum FieldBehaviour {
-    /// Default tracing behaviour.
+pub enum FieldBehavior {
+    /// Default tracing behavior.
     ///
     /// It means that field **should** be traced if nothing else restricting it.
     Default,
@@ -156,7 +189,7 @@ pub enum FieldBehaviour {
     Ignore,
 }
 
-impl FieldBehaviour {
+impl FieldBehavior {
     pub fn from_ident(ident: &syn::Ident) -> syn::Result<Self> {
         match ident.to_string().as_str() {
             "only" => Ok(Self::Only),
@@ -164,7 +197,7 @@ impl FieldBehaviour {
             _ => Err(syn::Error::new(
                 ident.span(),
                 format!(
-                    "Unknown tracing behaviour: got {}, supported values: only, ignore, skip",
+                    "Unknown tracing behavior: got {}, supported values: only, ignore, skip",
                     ident,
                 ),
             )),
@@ -195,7 +228,7 @@ pub trait TracedField {
     fn instrument(&self) -> Option<&Attr>;
 
     /// Returns [`FieldBehaviour`] parsed from `#[graphql(tracing = ...)]`
-    fn tracing_behaviour(&self) -> FieldBehaviour;
+    fn tracing_behavior(&self) -> FieldBehavior;
 
     /// Whether this field relies on async resolver.
     fn is_async(&self) -> bool;
@@ -217,10 +250,10 @@ fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
     let rule = ty.tracing_rule();
 
     match rule {
-        Rule::All => field.tracing_behaviour() != FieldBehaviour::Ignore,
-        Rule::Sync if !field.is_async() => field.tracing_behaviour() != FieldBehaviour::Ignore,
-        Rule::Async if field.is_async() => field.tracing_behaviour() != FieldBehaviour::Ignore,
-        Rule::Complex => field.tracing_behaviour() == FieldBehaviour::Only,
+        Rule::All => field.tracing_behavior() != FieldBehavior::Ignore,
+        Rule::Sync if !field.is_async() => field.tracing_behavior() != FieldBehavior::Ignore,
+        Rule::Async if field.is_async() => field.tracing_behavior() != FieldBehavior::Ignore,
+        Rule::Complex => field.tracing_behavior() == FieldBehavior::Only,
         _ => false,
     }
 }
@@ -322,7 +355,7 @@ mod graphql_object {
     };
 
     use super::{Attr, Rule, TracedArgument, TracedField, TracedType};
-    use crate::tracing::FieldBehaviour;
+    use crate::tracing::FieldBehavior;
 
     impl TracedType for GraphQLTypeDefinition {
         fn tracing_rule(&self) -> Rule {
@@ -345,8 +378,8 @@ mod graphql_object {
             self.instrument_attr.as_ref()
         }
 
-        fn tracing_behaviour(&self) -> FieldBehaviour {
-            self.tracing_behaviour.unwrap_or(FieldBehaviour::Default)
+        fn tracing_behavior(&self) -> FieldBehavior {
+            self.tracing_behavior.unwrap_or(FieldBehavior::Default)
         }
 
         fn name(&self) -> &str {
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 85b5e58fa..459d32084 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -475,7 +475,7 @@ enum FieldAttribute {
     Arguments(HashMap<String, FieldAttributeArgument>),
     Default(Box<SpanContainer<Option<syn::Expr>>>),
     #[cfg(feature = "tracing")]
-    Tracing(SpanContainer<tracing::FieldBehaviour>),
+    Tracing(SpanContainer<tracing::FieldBehavior>),
 }
 
 impl Parse for FieldAttribute {
@@ -554,11 +554,11 @@ impl Parse for FieldAttribute {
             "tracing" => {
                 let content;
                 syn::parenthesized!(content in input);
-                let behaviour = content.parse_any_ident()?;
-                tracing::FieldBehaviour::from_ident(&behaviour).map(|val| {
+                let behavior = content.parse_any_ident()?;
+                tracing::FieldBehavior::from_ident(&behavior).map(|val| {
                     FieldAttribute::Tracing(SpanContainer::new(
                         ident.span(),
-                        Some(behaviour.span()),
+                        Some(behavior.span()),
                         val,
                     ))
                 })
@@ -582,7 +582,7 @@ pub struct FieldAttributes {
 
     // Only relevant for GraphQLObject derive and graphql_object attribute.
     #[cfg(feature = "tracing")]
-    pub tracing: Option<tracing::FieldBehaviour>,
+    pub tracing: Option<tracing::FieldBehavior>,
 }
 
 impl Parse for FieldAttributes {
@@ -676,7 +676,7 @@ pub struct GraphQLTypeDefinitionField {
 
     // Relevant only for `#[graphql_object]` and `#[derive(GraphQLObject)]`
     #[cfg(feature = "tracing")]
-    pub tracing_behaviour: Option<tracing::FieldBehaviour>,
+    pub tracing_behavior: Option<tracing::FieldBehavior>,
     #[cfg(feature = "tracing")]
     pub instrument_attr: Option<tracing::Attr>,
 }

From 123a87b78082b4720fa03b19003d765c718f91b9 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 13 Aug 2021 16:07:41 +0300
Subject: [PATCH 66/72] Book and docs correction

---
 docs/book/content/tracing/index.md            |  94 +++++++++--
 examples/tracing_support/src/main.rs          |  13 +-
 integration_tests/juniper_tests/src/array.rs  |   4 +-
 juniper/src/tests/fixtures/tracing/schema.rs  |  60 +++----
 juniper_codegen/src/common/field/arg.rs       |   5 +-
 juniper_codegen/src/common/field/mod.rs       |  24 ++-
 juniper_codegen/src/common/parse/attr.rs      |  10 ++
 juniper_codegen/src/derive_enum.rs            |  10 --
 juniper_codegen/src/derive_input_object.rs    |   9 --
 juniper_codegen/src/graphql_interface/attr.rs |   6 +-
 juniper_codegen/src/graphql_interface/mod.rs  |  47 ++----
 .../src/graphql_interface/tracing.rs          |  66 --------
 juniper_codegen/src/graphql_object/attr.rs    |   3 +-
 juniper_codegen/src/graphql_object/derive.rs  |   5 +-
 juniper_codegen/src/graphql_object/mod.rs     |  41 ++---
 juniper_codegen/src/graphql_object/tracing.rs |  18 ---
 juniper_codegen/src/lib.rs                    |   4 +-
 juniper_codegen/src/tracing.rs                | 149 ++++++++++++------
 juniper_codegen/src/util/mod.rs               |  47 ------
 19 files changed, 281 insertions(+), 334 deletions(-)
 delete mode 100644 juniper_codegen/src/graphql_interface/tracing.rs
 delete mode 100644 juniper_codegen/src/graphql_object/tracing.rs

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index cbf21fe25..d4880065b 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -133,26 +133,58 @@ impl User {
 
 Manually setting `#[graphql(tracing(ignore))]` to avoid tracing of all, let's say for
 example synchronous field resolvers is rather inefficient when you have GraphQL
-object with too much fields. In this case you can use `tracing` argument on
-top-level `#[graphql_object]`, `#[graphql_interface]` or `#[graphql]` (when it
-used with `#[derive(GraphQLObject)]`) attributes to trace specific group of
-fields or not to trace at all. `tracing` argument can be used with one of the
-following arguments: `sync`, `async`, `only` or `skip_all`.
- - Use `sync` to trace only synchronous part (struct fields and `fn`s).
- - Use `async` to trace only asynchronous part (`async fn`s) and
+object with too much fields. In this case you can use `tracing` attribute on
+top-level to skip tracing of specific field group or not to trace at all.
+`tracing` attribute can be used with one of the following arguments:
+`sync`, `async`, `only` or `skip_all`.
+ - Use `sync` to trace only synchronous resolvers (struct fields and `fn`s).
+ - Use `async` to trace only asynchronous resolvers (`async fn`s) and
 subscriptions.
  - Use `only` to trace only fields marked with `#[graphql(tracing(only))]`
  - Use `skip_all` to skip tracing of all fields.
 
+### Example
+
+```rust
+# extern crate juniper;
+# use juniper::graphql_object;
+# fn main() {}
+
+struct MagicOfTracing;
+
+#[graphql_object]
+#[tracing(async)]
+impl MagicOfTracing {
+    // Won't produce span because it's sync resolver
+    fn my_sync_fn(&self) -> String {
+        "Woah sync resolver!!".to_owned()
+    }
+
+    // Will produce span `MagicOfTracing.myAsyncFn`.
+    async fn my_async_fn(&self) -> String {
+        "Woah async resolver with traces!!".to_owned()
+    }
+
+    // Won't produce span because even though this is an async resolver
+    // it's also marked with `#[graphql(tracing(ignore))]`.
+    #[graphql(tracing(ignore))]
+    async fn non_traced_async_fn(&self) -> String {
+        "Leave no traces".to_owned()
+    }
+}
+```
+
 **Note:** using of `tracing(sync)` with derived struct is no-op because all
 resolvers within derived GraphQL object is considered to be synchronous, also
 because of this `tracing(async)` will result in no traces.
 
-In addition you can use `#[graphql(tracing(ignore))]` with `skip` and `async`
+In addition you can use `#[graphql(tracing(ignore))]` with `sync` and `async`
 variants to exclude field from tracing even if it belongs to traced group.
 
 **Be careful when skipping trace as it can lead to bad structured span trees,
-disabling of tracing on one level won't disable tracing in it's child methods.**
+disabling of tracing on one level won't disable tracing in it's child methods.
+As a rule of thumb you should trace all field resolvers which may produce child
+spans.**
 
 If resolving of certain field requires additional arguments (when used `fn`s or
 `async fn`s) they also will be included in resulted trace (except `self` and
@@ -164,9 +196,6 @@ If resolving of certain field requires additional arguments (when used `fn`s or
 #
 # fn main() {}
 #
-# struct Context;
-# impl juniper::Context for Context {}
-#
 # type Filter = i32;
 #
 # #[derive(GraphQLObject)]
@@ -200,8 +229,6 @@ this field with `fields(field_name = some_value)` like shown bellow.
 #
 # fn main() {}
 #
-# struct Context;
-# impl juniper::Context for Context {}
 # struct Query;
 
 #[derive(Clone, juniper::GraphQLInputObject)]
@@ -220,9 +247,42 @@ fn my_query(&self, non_debug: NonDebug) -> i32 {
 # }
 ```
 
-Custom fields generated this way are context aware and can use both `context` and `self`
-even if they're not implicitly passed to resolver. In case when resolver is `fn` with not
-only `self` and `context` arguments they're also available to interact with as shown above.
+Custom fields generated this way are aware of `self` and can use `self` even if it not implicitly passed
+to resolver. In case when resolver is `fn` with not only `self` arguments they're also available
+to interact with as shown above. You can also access `executor` and `Context` as a result.
+
+### Example
+```rust
+# extern crate juniper;
+# use juniper::graphql_object;
+#
+# fn main() {}
+#
+struct Context {
+    data: i32,
+}
+
+impl juniper::Context for Context {}
+
+struct Query {
+    data: i32,
+}
+
+#[graphql_object(context = Context)]
+impl Query {
+#[instrument(fields(ctx.data = executor.context().data))]
+fn my_query(&self) -> i32 {
+    // Some query
+#    unimplemented!()
+}
+
+#[instrument(fields(data = self.data))]
+fn self_aware() -> i32 {
+    // Some query
+#   unimplemented!()
+}
+# }
+```
 
 ## `#[instrument]` attribute
 
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 3334f48c9..14c9803a7 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -31,7 +31,7 @@ struct User {
 #[graphql_object(Context = Context)]
 impl User {
     // `id` can be resolved pretty straightforward so we mark it with `no_trace`
-    #[tracing(no_trace)]
+    #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
     }
@@ -58,7 +58,8 @@ struct SyncTracedUser {
 
 // Only sync `fn`s will be traced if they're not marked with `#[tracing(no_trace)]`
 // it works similarly with `#[graphql_interface]`
-#[graphql_object(Context = Context, tracing(sync))]
+#[graphql_object(Context = Context)]
+#[tracing(sync)]
 impl SyncTracedUser {
     // Won't be traced because it's marked with `no_trace`
     #[graphql(tracing(ignore))]
@@ -78,14 +79,14 @@ impl SyncTracedUser {
 }
 
 #[derive(Clone, Debug, GraphQLObject)]
-#[graphql(tracing(only))]
+#[tracing(only)]
 struct ComplexDerivedUser {
-    // This shouldn't be traced because it's not marked with `#[tracing(complex)]`
+    // This shouldn't be traced because it's not marked with `#[tracing(only)]`
     id: i32,
-    // This is the only field that will be traced because it's marked with `#[tracing(complex)]`
+    // This is the only field that will be traced because it's marked with `#[tracing(only)]`
     #[graphql(tracing(only))]
     kind: UserKind,
-    // This shouldn't be traced because of `no_trace`.
+    // This shouldn't be traced because of `ignore`.
     #[graphql(tracing(ignore))]
     name: String,
 }
diff --git a/integration_tests/juniper_tests/src/array.rs b/integration_tests/juniper_tests/src/array.rs
index aa1606511..762c23bdf 100644
--- a/integration_tests/juniper_tests/src/array.rs
+++ b/integration_tests/juniper_tests/src/array.rs
@@ -36,12 +36,12 @@ mod as_output_field {
 mod as_input_field {
     use super::*;
 
-    #[derive(GraphQLInputObject)]
+    #[derive(Debug, GraphQLInputObject)]
     struct Input {
         two: [bool; 2],
     }
 
-    #[derive(GraphQLInputObject)]
+    #[derive(Debug, GraphQLInputObject)]
     struct InputSingle {
         one: [bool; 1],
     }
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index b15846f0e..ac19ead7d 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -390,10 +390,8 @@ pub trait FooBar {
 /// GraphQL object marked with `tracing(sync)`.
 pub struct TraceSync;
 
-#[graphql_object(
-    tracing(sync),
-    impl = [InterfacedSimpleValue, InterfacedSyncValue],
-)]
+#[graphql_object(impl = [InterfacedSimpleValue, InterfacedSyncValue])]
+#[tracing(sync)]
 impl TraceSync {
     pub fn sync_fn(&self) -> i32 {
         1
@@ -409,7 +407,7 @@ build_impl!(TraceSync, InterfacedSync);
 
 /// Derived GraphQL object marked with `tracing(sync)`.
 #[derive(Default, GraphQLObject)]
-#[graphql(tracing(sync))]
+#[tracing(sync)]
 pub struct SyncDerived {
     /// Simple field
     sync: i32,
@@ -418,10 +416,8 @@ pub struct SyncDerived {
 /// GraphQL object marked with `tracing(async)`.
 pub struct TraceAsync;
 
-#[graphql_object(
-    tracing(async),
-    impl = [InterfacedAsyncValue],
-)]
+#[graphql_object(impl = [InterfacedAsyncValue])]
+#[tracing(async)]
 impl TraceAsync {
     pub fn sync_fn(&self) -> i32 {
         1
@@ -436,7 +432,7 @@ build_impl!(TraceAsync, InterfacedAsync);
 
 /// Derived GraphQL object.
 #[derive(Default, GraphQLObject)]
-#[graphql(tracing(async))]
+#[tracing(async)]
 pub struct AsyncDerived {
     /// Simple field
     sync: i32,
@@ -445,10 +441,8 @@ pub struct AsyncDerived {
 /// GraphQL object marked with `tracing(skip_all)`.
 pub struct SkipAll;
 
-#[graphql_object(
-    tracing(skip_all),
-    impl = [InterfacedSkipAllValue],
-)]
+#[graphql_object(impl = [InterfacedSkipAllValue])]
+#[tracing(skip_all)]
 impl SkipAll {
     pub fn sync_fn(&self) -> i32 {
         1
@@ -463,7 +457,7 @@ build_impl!(SkipAll, InterfacedSkipAll);
 
 /// Derived GraphQL object marked with `tracing(skip_all)`.
 #[derive(Default, GraphQLObject)]
-#[graphql(tracing(skip_all))]
+#[tracing(skip_all)]
 pub struct SkipAllDerived {
     /// Simple field
     sync: i32,
@@ -472,10 +466,8 @@ pub struct SkipAllDerived {
 /// GraphQL object marked with `tracing(only)`.
 pub struct Complex;
 
-#[graphql_object(
-    tracing(only),
-    impl = [InterfacedComplexValue],
-)]
+#[graphql_object(impl = [InterfacedComplexValue])]
+#[tracing(only)]
 impl Complex {
     #[graphql(tracing(only))]
     pub fn sync_fn(&self) -> i32 {
@@ -496,7 +488,7 @@ build_impl!(Complex, InterfacedComplex);
 
 /// Derived GraphQL object marked with `tracing(only)`.
 #[derive(GraphQLObject)]
-#[graphql(tracing(only))]
+#[tracing(only)]
 pub struct DerivedComplex {
     #[graphql(tracing(only))]
     complex: bool,
@@ -517,41 +509,29 @@ trait InterfacedSimple {
     async fn async_fn(&self) -> i32;
 }
 
-#[graphql_interface(
-    for = [TraceSync],
-    tracing(sync),
-    async,
-)]
+#[graphql_interface(for = [TraceSync], async)]
+#[tracing(sync)]
 trait InterfacedSync {
     fn sync_fn(&self) -> i32;
     async fn async_fn(&self) -> i32;
 }
 
-#[graphql_interface(
-    for = [TraceAsync],
-    tracing(async),
-    async,
-)]
+#[graphql_interface(for = [TraceAsync], async)]
+#[tracing(async)]
 trait InterfacedAsync {
     fn sync_fn(&self) -> i32;
     async fn async_fn(&self) -> i32;
 }
 
-#[graphql_interface(
-    for = [SkipAll],
-    tracing(skip_all),
-    async,
-)]
+#[graphql_interface(for = [SkipAll], async)]
+#[tracing(skip_all)]
 trait InterfacedSkipAll {
     fn sync_fn(&self) -> i32;
     async fn async_fn(&self) -> i32;
 }
 
-#[graphql_interface(
-    for = [Complex],
-    tracing(only),
-    async,
-)]
+#[graphql_interface(for = [Complex], async)]
+#[tracing(only)]
 trait InterfacedComplex {
     fn sync_fn(&self) -> i32;
     #[graphql(tracing(only))]
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index feabc0e76..db2644fb9 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -365,9 +365,12 @@ impl OnMethod {
         }
     }
 
+    /// Returns generated code for the [`GraphQLValue::resolve_field`] method,
+    /// which assigns the value of this [`OnMethod`] argument to local variable.
+    ///
+    /// [`GraphQLValue::resolve_field`]: juniper::GraphQLValue::resolve_field
     #[must_use]
     pub(crate) fn method_resolve_arg_getter_tokens(&self, scalar: &scalar::Type) -> TokenStream {
-
         match self {
             Self::Regular(arg) => {
                 let (name, raw_name, ty) = (&arg.name, &arg.raw_name, &arg.ty);
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 8d6c90476..426c82ad3 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -25,6 +25,7 @@ use crate::{
     },
     util::{filter_attrs, get_deprecated, get_doc_comment, span_container::SpanContainer},
 };
+
 #[cfg(feature = "tracing")]
 use crate::tracing;
 
@@ -82,6 +83,10 @@ pub(crate) struct Attr {
     /// [2]: https://spec.graphql.org/June2018/#sec-Objects
     pub(crate) downcast: Option<SpanContainer<syn::Ident>>,
 
+    /// Explicitly specified tracing behavior of this [GraphQL field][1] resolver
+    /// that used to define whether this it should be traced or not.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
     pub(crate) tracing_behavior: Option<SpanContainer<tracing::FieldBehavior>>,
 }
@@ -134,9 +139,17 @@ impl Parse for Attr {
                     syn::parenthesized!(content in input);
                     let behavior = content.parse_any_ident()?;
                     out.tracing_behavior
-                        .replace(SpanContainer::new(ident.span(), Some(behavior.span()), tracing::FieldBehavior::from_ident(&behavior)?))
+                        .replace(SpanContainer::new(
+                            ident.span(),
+                            Some(behavior.span()),
+                            tracing::FieldBehavior::from_ident(&behavior)?,
+                        ))
                         .none_or_else(|_| err::dup_arg(&ident))?;
                 },
+                #[cfg(not(feature = "tracing"))]
+                "tracing" => {
+                    return Err(err::tracing_disabled(&ident));
+                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -157,6 +170,7 @@ impl Attr {
             deprecated: try_merge_opt!(deprecated: self, another),
             ignore: try_merge_opt!(ignore: self, another),
             downcast: try_merge_opt!(downcast: self, another),
+            #[cfg(feature = "tracing")]
             tracing_behavior: try_merge_opt!(tracing_behavior: self, another),
         })
     }
@@ -272,9 +286,17 @@ pub(crate) struct Definition {
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     pub(crate) is_async: bool,
 
+    /// Optional `#[instrument]` attribute placed on top of this [GraphQL field][1]
+    /// resolver.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
     pub(crate) instrument: Option<tracing::Attr>,
 
+    /// Explicitly specified tracing behavior of this [GraphQL field][1] resolver
+    /// that affects whether this it should be traced or not.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
     pub(crate) tracing: Option<tracing::FieldBehavior>,
 }
diff --git a/juniper_codegen/src/common/parse/attr.rs b/juniper_codegen/src/common/parse/attr.rs
index 9e42cc975..0535c92fd 100644
--- a/juniper_codegen/src/common/parse/attr.rs
+++ b/juniper_codegen/src/common/parse/attr.rs
@@ -53,6 +53,16 @@ pub(crate) mod err {
         )
     }
 
+    /// Creates "tracing feature disabled" [`syn::Error`] pointing to the given `span`.
+    #[cfg(not(feature = "tracing"))]
+    #[must_use]
+    pub(crate) fn tracing_disabled<S: AsSpan>(span: S) -> syn::Error {
+        syn::Error::new(
+            span.as_span(),
+            "`tracing` feature disabled",
+        )
+    }
+
     /// Helper coercion for [`Span`] and [`Spanned`] types to use in function arguments.
     pub(crate) trait AsSpan {
         /// Returns the coerced [`Span`].
diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs
index 615e92eed..0201f09b9 100644
--- a/juniper_codegen/src/derive_enum.rs
+++ b/juniper_codegen/src/derive_enum.rs
@@ -99,12 +99,6 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
                 is_async: false,
                 default: None,
                 span,
-
-                // Enums cannot be traced.
-                #[cfg(feature = "tracing")]
-                tracing_behavior: None,
-                #[cfg(feature = "tracing")]
-                instrument_attr: None,
             })
         })
         .collect::<Vec<_>>();
@@ -153,10 +147,6 @@ pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<Toke
         include_type_generics: true,
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
-
-        // NOTICE: nothing to trace on enums
-        #[cfg(feature = "tracing")]
-        tracing_rule: None,
     };
 
     Ok(definition.into_enum_tokens())
diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs
index 6e06833c2..9992f039a 100644
--- a/juniper_codegen/src/derive_input_object.rs
+++ b/juniper_codegen/src/derive_input_object.rs
@@ -95,12 +95,6 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
                 is_async: false,
                 default,
                 span,
-
-                // Tracing of GraphQL Input Objects not supported
-                #[cfg(feature = "tracing")]
-                tracing_behavior: None,
-                #[cfg(feature = "tracing")]
-                instrument_attr: None,
             })
         })
         .collect::<Vec<_>>();
@@ -151,9 +145,6 @@ pub fn impl_input_object(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Res
         include_type_generics: true,
         generic_scalar: true,
         no_async: attrs.no_async.is_some(),
-
-        #[cfg(feature = "tracing")]
-        tracing_rule: None,
     };
 
     Ok(definition.into_input_object_tokens())
diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs
index cd9d09740..a71981063 100644
--- a/juniper_codegen/src/graphql_interface/attr.rs
+++ b/juniper_codegen/src/graphql_interface/attr.rs
@@ -32,6 +32,10 @@ pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStr
     if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body.clone()) {
         let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
         ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
+        #[cfg(feature = "tracing")]
+        {
+            ast.attrs = parse::attr::strip("tracing", ast.attrs);
+        }
         return expand_on_trait(trait_attrs, ast);
     } else if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
         if ast.trait_.is_some() {
@@ -203,7 +207,7 @@ fn expand_on_trait(
         implementers,
 
         #[cfg(feature = "tracing")]
-        tracing_rule: attr.tracing_rule.map(|t| t.into_inner()),
+        tracing_rule: tracing::Rule::from_attrs("tracing", &attrs)?,
     };
 
     // Attach the `juniper::AsDynGraphQLValue` on top of the trait if dynamic dispatch is used.
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index 8b897e4ac..e0d9d00c9 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -3,8 +3,6 @@
 //! [1]: https://spec.graphql.org/June2018/#sec-Interfaces
 
 pub mod attr;
-#[cfg(feature = "tracing")]
-mod tracing;
 
 use std::{
     collections::{HashMap, HashSet},
@@ -32,6 +30,8 @@ use crate::{
     },
     util::{filter_attrs, get_doc_comment, span_container::SpanContainer, RenameRule},
 };
+#[cfg(feature = "tracing")]
+use crate::tracing;
 
 /// Available arguments behind `#[graphql_interface]` attribute placed on a
 /// trait definition, when generating code for [GraphQL interface][1] type.
@@ -249,32 +249,6 @@ impl Parse for TraitAttr {
                 "internal" => {
                     out.is_internal = true;
                 }
-                #[cfg(feature = "tracing")]
-                "tracing" => {
-                    use std::str::FromStr as _;
-
-                    use proc_macro_error::abort;
-
-                    let span = ident.span();
-                    let content;
-                    syn::parenthesized!(content in input);
-                    let tracing = content.parse_any_ident()?;
-                    let tracing_rule = tracing::Rule::from_str(tracing.to_string().as_str());
-                    match tracing_rule {
-                        Ok(rule) => out
-                            .tracing_rule
-                            .replace(SpanContainer::new(span, Some(tracing.span()), rule))
-                            .none_or_else(|_| err::dup_arg(span))?,
-                        Err(_) => abort!(syn::Error::new(
-                            tracing.span(),
-                            format!(
-                                "Unknown tracing rule: {}, \
-                                 known values: sync, async, skip-all and complex",
-                                tracing,
-                            )
-                        )),
-                    }
-                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -430,7 +404,7 @@ impl ImplAttr {
 /// Definition of [GraphQL interface][1] for code generation.
 ///
 /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-struct Definition {
+pub(crate) struct Definition {
     /// Rust type that this [GraphQL interface][1] is represented with.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
@@ -439,12 +413,12 @@ struct Definition {
     /// Name of this [GraphQL interface][1] in GraphQL schema.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    name: String,
+    pub(crate) name: String,
 
     /// Description of this [GraphQL interface][1] to put into GraphQL schema.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    description: Option<String>,
+    pub(crate) description: Option<String>,
 
     /// Rust type of [`Context`] to generate [`GraphQLType`] implementation with
     /// for this [GraphQL interface][1].
@@ -452,7 +426,7 @@ struct Definition {
     /// [`GraphQLType`]: juniper::GraphQLType
     /// [`Context`]: juniper::Context
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    context: syn::Type,
+    pub(crate) context: syn::Type,
 
     /// [`ScalarValue`] parametrization to generate [`GraphQLType`]
     /// implementation with for this [GraphQL interface][1].
@@ -460,13 +434,13 @@ struct Definition {
     /// [`GraphQLType`]: juniper::GraphQLType
     /// [`ScalarValue`]: juniper::ScalarValue
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    scalar: scalar::Type,
+    pub(crate) scalar: scalar::Type,
 
     /// Defined [GraphQL fields][2] of this [GraphQL interface][1].
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    fields: Vec<field::Definition>,
+    pub(crate) fields: Vec<field::Definition>,
 
     /// Defined [`Implementer`]s of this [GraphQL interface][1].
     ///
@@ -478,8 +452,11 @@ struct Definition {
     ///
     /// If it's absent and `tracing` feature is enabled all [field][2]s not marked
     /// with `#[tracing(no_trace)]` will be traced.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
-    tracing_rule: Option<tracing::Rule>,
+    pub(crate) tracing_rule: tracing::Rule,
 }
 
 impl ToTokens for Definition {
diff --git a/juniper_codegen/src/graphql_interface/tracing.rs b/juniper_codegen/src/graphql_interface/tracing.rs
deleted file mode 100644
index 957cce64b..000000000
--- a/juniper_codegen/src/graphql_interface/tracing.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-pub use crate::{
-    tracing::{
-        async_tokens, span_tokens, sync_tokens, Attr, FieldBehavior, Rule, TracedArgument,
-        TracedField, TracedType,
-    }
-};
-
-use super::{Definition as InterfaceDefinition, field};
-
-impl TracedType for InterfaceDefinition {
-    fn tracing_rule(&self) -> Rule {
-        self.tracing_rule.unwrap_or(Rule::All)
-    }
-
-    fn name(&self) -> &str {
-        self.name.as_str()
-    }
-
-    fn scalar(&self) -> Option<syn::Type> {
-        Some(self.scalar.ty())
-    }
-}
-
-impl TracedField for field::Definition {
-    type Arg = field::arg::OnField;
-
-    fn instrument(&self) -> Option<&Attr> {
-        self.instrument.as_ref()
-    }
-
-    fn tracing_behavior(&self) -> FieldBehavior {
-        self.tracing.unwrap_or(FieldBehavior::Default)
-    }
-
-    fn is_async(&self) -> bool {
-        self.is_async
-    }
-
-    fn name(&self) -> &str {
-        self.name.as_str()
-    }
-
-    fn args(&self) -> Vec<&Self::Arg> {
-        self.arguments
-            .as_ref()
-            .map_or_else(
-                || vec![],
-                |args| args.iter()
-                    .filter_map(|arg| arg.as_regular())
-                    .collect())
-    }
-}
-
-impl TracedArgument for field::arg::OnField {
-    fn ty(&self) -> &syn::Type {
-        &self.ty
-    }
-
-    fn name(&self) -> &str {
-        self.name.as_str()
-    }
-
-    fn raw_name(&self) -> &syn::Ident {
-        &self.raw_name
-    }
-}
diff --git a/juniper_codegen/src/graphql_object/attr.rs b/juniper_codegen/src/graphql_object/attr.rs
index 51315c33d..b02b99552 100644
--- a/juniper_codegen/src/graphql_object/attr.rs
+++ b/juniper_codegen/src/graphql_object/attr.rs
@@ -15,6 +15,7 @@ use crate::{
     result::GraphQLScope,
     util::{path_eq_single, span_container::SpanContainer, RenameRule},
 };
+
 #[cfg(feature = "tracing")]
 use crate::tracing;
 
@@ -132,7 +133,7 @@ where
         _operation: PhantomData,
 
         #[cfg(feature = "tracing")]
-        tracing: attr.tracing_rule.map(|t| t.into_inner())
+        tracing: tracing::Rule::from_attrs_and_strip("tracing", &mut ast.attrs)?,
     };
 
     Ok(quote! {
diff --git a/juniper_codegen/src/graphql_object/derive.rs b/juniper_codegen/src/graphql_object/derive.rs
index 5fa07ecd8..350e50d25 100644
--- a/juniper_codegen/src/graphql_object/derive.rs
+++ b/juniper_codegen/src/graphql_object/derive.rs
@@ -12,6 +12,7 @@ use crate::{
     result::GraphQLScope,
     util::{span_container::SpanContainer, RenameRule},
 };
+
 #[cfg(feature = "tracing")]
 use crate::tracing;
 
@@ -33,7 +34,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
 
 /// Expands into generated code a `#[derive(GraphQLObject)]` macro placed on a
 /// Rust struct.
-fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
+fn expand_struct(mut ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
     let attr = Attr::from_attrs("graphql", &ast.attrs)?;
 
     let struct_span = ast.span();
@@ -109,7 +110,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
         _operation: PhantomData,
 
         #[cfg(feature = "tracing")]
-        tracing: attr.tracing_rule.map(|t| t.into_inner()),
+        tracing: tracing::Rule::from_attrs_and_strip("tracing", &mut ast.attrs)?,
     })
 }
 
diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs
index 0a2d256a7..db1bb2d09 100644
--- a/juniper_codegen/src/graphql_object/mod.rs
+++ b/juniper_codegen/src/graphql_object/mod.rs
@@ -5,12 +5,8 @@
 pub mod attr;
 pub mod derive;
 
-#[cfg(feature = "tracing")]
-mod tracing;
-
-use std::{any::TypeId, collections::HashSet, convert::TryInto as _, marker::PhantomData, str::FromStr as _};
+use std::{any::TypeId, collections::HashSet, convert::TryInto as _, marker::PhantomData};
 
-use proc_macro_error::abort;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
 use syn::{
@@ -33,6 +29,9 @@ use crate::{
     util::{filter_attrs, get_doc_comment, span_container::SpanContainer, RenameRule},
 };
 
+#[cfg(feature = "tracing")]
+use crate::tracing;
+
 /// Available arguments behind `#[graphql]` (or `#[graphql_object]`) attribute
 /// when generating code for [GraphQL object][1] type.
 ///
@@ -95,9 +94,6 @@ pub(crate) struct Attr {
     /// Indicator whether the generated code is intended to be used only inside
     /// the [`juniper`] library.
     pub(crate) is_internal: bool,
-
-    #[cfg(feature = "tracing")]
-    pub(crate) tracing_rule: Option<SpanContainer<tracing::Rule>>
 }
 
 impl Parse for Attr {
@@ -168,27 +164,6 @@ impl Parse for Attr {
                 "internal" => {
                     out.is_internal = true;
                 }
-                #[cfg(feature = "tracing")]
-                "tracing" => {
-                    let content;
-                    syn::parenthesized!(content in input);
-                    let tracing = content.parse_any_ident()?;
-                    let tracing_rule = tracing::Rule::from_str(tracing.to_string().as_str());
-                    match tracing_rule {
-                        Ok(rule) => out
-                            .tracing_rule
-                            .replace(SpanContainer::new(ident.span(), Some(tracing.span()), rule))
-                            .none_or_else(|_| err::dup_arg(ident.span()))?,
-                        Err(_) => abort!(syn::Error::new(
-                            tracing.span(),
-                            format!(
-                                "Unknown tracing rule: {}, \
-                                 known values: sync, async, skip-all and complex",
-                                tracing,
-                            )
-                        )),
-                    }
-                }
                 name => {
                     return Err(err::unknown_arg(&ident, name));
                 }
@@ -211,7 +186,6 @@ impl Attr {
             interfaces: try_merge_hashset!(interfaces: self, another => span_joined),
             rename_fields: try_merge_opt!(rename_fields: self, another),
             is_internal: self.is_internal || another.is_internal,
-            tracing_rule: try_merge_opt!(tracing_rule: self, another),
         })
     }
 
@@ -295,8 +269,13 @@ pub(crate) struct Definition<Operation: ?Sized> {
     /// [3]: https://spec.graphql.org/June2018/#sec-Subscription
     pub(crate) _operation: PhantomData<Box<Operation>>,
 
+    /// Explicitly specified rule, that used to define which [`GraphQL field`][1]s
+    /// of this [`GraphQL object`][2] should be traced.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     #[cfg(feature = "tracing")]
-    pub(crate) tracing: Option<tracing::Rule>,
+    pub(crate) tracing: tracing::Rule,
 }
 
 impl<Operation: ?Sized + 'static> Definition<Operation> {
diff --git a/juniper_codegen/src/graphql_object/tracing.rs b/juniper_codegen/src/graphql_object/tracing.rs
deleted file mode 100644
index a773a3174..000000000
--- a/juniper_codegen/src/graphql_object/tracing.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-pub use crate::tracing::*;
-
-use super::Definition;
-use syn::Type;
-
-impl<T: ?Sized> TracedType for Definition<T> {
-    fn tracing_rule(&self) -> Rule {
-        self.tracing.unwrap_or(Rule::All)
-    }
-
-    fn name(&self) -> &str {
-        &self.name
-    }
-
-    fn scalar(&self) -> Option<Type> {
-        Some(self.scalar.ty())
-    }
-}
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index d45b34ace..aa069215b 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -208,7 +208,7 @@ pub fn derive_input_object(input: TokenStream) -> TokenStream {
 /// TODO: write documentation.
 ///
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLScalarValue, attributes(graphql, instrument))]
+#[proc_macro_derive(GraphQLScalarValue, attributes(graphql))]
 pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
     let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
     let gen = derive_scalar_value::impl_scalar_value(&ast, GraphQLScope::DeriveScalar);
@@ -817,7 +817,7 @@ pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
 /// [`ScalarValue`]: juniper::ScalarValue
 /// [1]: https://spec.graphql.org/June2018/#sec-Objects
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLObject, attributes(graphql, instrument))]
+#[proc_macro_derive(GraphQLObject, attributes(graphql, instrument, tracing))]
 pub fn derive_object(body: TokenStream) -> TokenStream {
     self::graphql_object::derive::expand(body.into())
         .unwrap_or_abort()
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index 6f9f41f63..baf8b35ad 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -1,4 +1,4 @@
-use std::{collections::HashMap, mem, str::FromStr};
+use std::{collections::HashMap, mem};
 
 use proc_macro2::TokenStream;
 use proc_macro_error::abort;
@@ -9,8 +9,11 @@ use syn::{
     token,
 };
 
+use crate::common::parse::ParseBufferExt as _;
+
 pub const ATTR_NAME: &'static str = "instrument";
 
+/// `#[instrument]` attribute placed on field resolver.
 #[derive(Debug, Default)]
 pub struct Attr {
     /// Optional span rename, if `None` method name should be used instead.
@@ -30,8 +33,8 @@ pub struct Attr {
 }
 
 impl Attr {
-    /// Parses [`TracingAttr`] from `method`s attributes and removes itself from
-    /// `method.attrs` if present.
+    /// Parses [`Attr`] from trait `method`s attributes and removes itself
+    /// from `method.attrs` if present.
     pub fn from_trait_method(method: &mut syn::TraitItemMethod) -> Option<Self> {
         let attr = method
             .attrs
@@ -51,6 +54,8 @@ impl Attr {
         }
     }
 
+    /// Parses [`Attr`] from impl `method`s attributes and removes itself
+    /// from `method.attrs` if present.
     pub fn from_method(method: &mut syn::ImplItemMethod) -> Option<Self> {
         let attr = method
             .attrs
@@ -70,6 +75,7 @@ impl Attr {
         }
     }
 
+    /// Parses [`Attr`] from structure `field`s attributes if present.
     pub fn from_field(field: &syn::Field) -> Option<Self> {
         let attr = field
             .attrs
@@ -146,29 +152,65 @@ pub enum Rule {
     /// Trace all fields that can be synchronously resolved.
     Sync,
 
-    /// Trace only fields that marked with `#[tracing(complex)]`.
-    Complex,
+    /// Trace only fields that marked with `#[tracing(only)]`.
+    Only,
 
     /// Skip tracing of all fields.
     SkipAll,
 }
 
+impl Rule {
+    /// Constructs [`Rule`] from attribute with given name. If attribute with
+    /// `attr_name` is not present then returns default [`Rule`].
+    pub fn from_attrs(attr_name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
+        Ok(
+            attrs.iter()
+                .find_map(|attr| attr.path.is_ident(attr_name)
+                    .then(|| attr.parse_args()))
+                .transpose()?
+                .unwrap_or_else(Self::default)
+        )
+    }
+
+    /// Constructs [`Rule`] from attribute with given name, and strips it from list.
+    /// If attribute with `attr_name` is not present then returns default [`Rule`].
+    pub fn from_attrs_and_strip(
+        attr_name: &str,
+        attrs: &mut Vec<syn::Attribute>,
+    ) -> syn::Result<Self> {
+        let attr = Self::from_attrs(attr_name, &attrs)?;
+        *attrs = std::mem::take(attrs)
+            .into_iter()
+            .filter(|attr| !attr.path.is_ident(attr_name))
+            .collect();
+        Ok(attr)
+    }
+}
+
 impl Default for Rule {
     fn default() -> Self {
         Self::All
     }
 }
 
-impl FromStr for Rule {
-    type Err = ();
-
-    fn from_str(rule: &str) -> Result<Self, Self::Err> {
-        match rule {
+impl Parse for Rule {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        let ident = input.parse_any_ident()?;
+        match ident.to_string().as_str() {
             "async" => Ok(Self::Async),
             "sync" => Ok(Self::Sync),
             "skip_all" => Ok(Self::SkipAll),
-            "only" => Ok(Self::Complex),
-            _ => Err(()),
+            "only" => Ok(Self::Only),
+            tracing => Err(
+                syn::Error::new(
+                    ident.span(),
+                    format!(
+                        "Unknown tracing rule: {}, \
+                         known values: sync, async, skip-all and complex",
+                        tracing,
+                    )
+                )
+            ),
         }
     }
 }
@@ -190,6 +232,7 @@ pub enum FieldBehavior {
 }
 
 impl FieldBehavior {
+    /// Tries to construct [`FieldBehaviour`] from [`syn::Ident`].
     pub fn from_ident(ident: &syn::Ident) -> syn::Result<Self> {
         match ident.to_string().as_str() {
             "only" => Ok(Self::Only),
@@ -207,9 +250,8 @@ impl FieldBehavior {
 
 /// Generalisation of type that can be traced.
 pub trait TracedType {
-    /// Optional [`TracingRule`] read from attributes `#[graphql_object(trace = "...")]`
-    /// on impl block, `#[graphql(trace = "...")]` on derived GraphQLObject or
-    /// `#[graphql_interface(trace = "...")]` on trait definition.
+    /// Optional [`Rule`] read from attributes `#[tracing(...)]` object or interface
+    /// definition.
     fn tracing_rule(&self) -> Rule;
 
     /// Name of this type.
@@ -227,7 +269,7 @@ pub trait TracedField {
     /// Returns parsed `#[tracing]` attribute.
     fn instrument(&self) -> Option<&Attr>;
 
-    /// Returns [`FieldBehaviour`] parsed from `#[graphql(tracing = ...)]`
+    /// Returns [`FieldBehaviour`] parsed from `#[graphql(tracing(...))]`
     fn tracing_behavior(&self) -> FieldBehavior;
 
     /// Whether this field relies on async resolver.
@@ -240,12 +282,16 @@ pub trait TracedField {
     fn args(&self) -> Vec<&Self::Arg>;
 }
 
+/// Argument of traced field resolver.
 pub trait TracedArgument {
-    fn ty(&self) -> &syn::Type;
+    /// Name of the argument in camel case.
     fn name(&self) -> &str;
+
+    /// Raw name identifier, parsed from `fn`s args.
     fn raw_name(&self) -> &syn::Ident;
 }
 
+/// Checks whether the `field` of `ty` should be traced.
 fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
     let rule = ty.tracing_rule();
 
@@ -253,12 +299,12 @@ fn is_traced(ty: &impl TracedType, field: &impl TracedField) -> bool {
         Rule::All => field.tracing_behavior() != FieldBehavior::Ignore,
         Rule::Sync if !field.is_async() => field.tracing_behavior() != FieldBehavior::Ignore,
         Rule::Async if field.is_async() => field.tracing_behavior() != FieldBehavior::Ignore,
-        Rule::Complex => field.tracing_behavior() == FieldBehavior::Only,
+        Rule::Only => field.tracing_behavior() == FieldBehavior::Only,
         _ => false,
     }
 }
 
-// Returns code that constructs `span` required for tracing
+/// Returns code that constructs `span` required for tracing
 pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
         return quote!();
@@ -271,7 +317,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     let args = field.args().into_iter().filter_map(|arg| {
         let name = arg.name();
         let raw_name = arg.raw_name();
-        let arg_name = syn::LitStr::new(name, arg.ty().span());
+        let arg_name = syn::LitStr::new(name, raw_name.span());
 
         field
             .instrument()
@@ -331,7 +377,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     )
 }
 
-// Returns code to start tracing of async future
+/// Returns code to start tracing of async future
 pub fn async_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
         return quote!();
@@ -341,7 +387,7 @@ pub fn async_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStre
     )
 }
 
-// Returns code to start tracing of sync block
+/// Returns code to start tracing of sync block
 pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
         return quote!();
@@ -349,17 +395,28 @@ pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     quote!(let _tracing_guard = _tracing_span.enter();)
 }
 
-mod graphql_object {
-    use crate::util::{
-        GraphQLTypeDefinition, GraphQLTypeDefinitionField, GraphQLTypeDefinitionFieldArg,
-    };
+mod impls {
+    use crate::{common::field, graphql_interface as interface, graphql_object as object};
 
-    use super::{Attr, Rule, TracedArgument, TracedField, TracedType};
-    use crate::tracing::FieldBehavior;
+    use super::{Attr, FieldBehavior, Rule, TracedArgument, TracedField, TracedType};
 
-    impl TracedType for GraphQLTypeDefinition {
+    impl<T: ?Sized> TracedType for object::Definition<T> {
         fn tracing_rule(&self) -> Rule {
-            self.tracing_rule.unwrap_or(Rule::All)
+            self.tracing
+        }
+
+        fn name(&self) -> &str {
+            &self.name
+        }
+
+        fn scalar(&self) -> Option<syn::Type> {
+            Some(self.scalar.ty())
+        }
+    }
+
+    impl TracedType for interface::Definition {
+        fn tracing_rule(&self) -> Rule {
+            self.tracing_rule
         }
 
         fn name(&self) -> &str {
@@ -367,19 +424,23 @@ mod graphql_object {
         }
 
         fn scalar(&self) -> Option<syn::Type> {
-            self.scalar.clone()
+            Some(self.scalar.ty())
         }
     }
 
-    impl TracedField for GraphQLTypeDefinitionField {
-        type Arg = GraphQLTypeDefinitionFieldArg;
+    impl TracedField for field::Definition {
+        type Arg = field::arg::OnField;
 
         fn instrument(&self) -> Option<&Attr> {
-            self.instrument_attr.as_ref()
+            self.instrument.as_ref()
         }
 
         fn tracing_behavior(&self) -> FieldBehavior {
-            self.tracing_behavior.unwrap_or(FieldBehavior::Default)
+            self.tracing.unwrap_or(FieldBehavior::Default)
+        }
+
+        fn is_async(&self) -> bool {
+            self.is_async
         }
 
         fn name(&self) -> &str {
@@ -387,19 +448,17 @@ mod graphql_object {
         }
 
         fn args(&self) -> Vec<&Self::Arg> {
-            self.args.iter().collect()
-        }
-
-        fn is_async(&self) -> bool {
-            self.is_async
+            self.arguments
+                .as_ref()
+                .map_or_else(
+                    || vec![],
+                    |args| args.iter()
+                        .filter_map(|arg| arg.as_regular())
+                        .collect())
         }
     }
 
-    impl TracedArgument for GraphQLTypeDefinitionFieldArg {
-        fn ty(&self) -> &syn::Type {
-            &self._type
-        }
-
+    impl TracedArgument for field::arg::OnField {
         fn name(&self) -> &str {
             self.name.as_str()
         }
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 459d32084..229cf952d 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -19,8 +19,6 @@ use syn::{
 };
 
 use crate::common::parse::ParseBufferExt as _;
-#[cfg(feature = "tracing")]
-use crate::tracing;
 
 /// Compares a path to a one-segment string value,
 /// return true if equal.
@@ -304,9 +302,6 @@ pub struct ObjectAttributes {
     pub no_async: Option<SpanContainer<()>>,
     pub is_internal: bool,
     pub rename: Option<RenameRule>,
-
-    #[cfg(feature = "tracing")]
-    pub tracing_rule: Option<tracing::Rule>,
 }
 
 impl Parse for ObjectAttributes {
@@ -372,17 +367,6 @@ impl Parse for ObjectAttributes {
                     input.parse::<token::Eq>()?;
                     output.rename = Some(input.parse::<RenameRule>()?);
                 }
-                #[cfg(feature = "tracing")]
-                "tracing" => {
-                    let content;
-                    syn::parenthesized!(content in input);
-                    let val = content.parse_any_ident()?;
-                    if let Ok(trace) = tracing::Rule::from_str(&val.to_string()) {
-                        output.tracing_rule = Some(trace);
-                    } else {
-                        return Err(syn::Error::new(val.span(), "unknown tracing skip rule"));
-                    }
-                }
                 _ => {
                     return Err(syn::Error::new(ident.span(), "unknown attribute"));
                 }
@@ -474,8 +458,6 @@ enum FieldAttribute {
     Skip(SpanContainer<syn::Ident>),
     Arguments(HashMap<String, FieldAttributeArgument>),
     Default(Box<SpanContainer<Option<syn::Expr>>>),
-    #[cfg(feature = "tracing")]
-    Tracing(SpanContainer<tracing::FieldBehavior>),
 }
 
 impl Parse for FieldAttribute {
@@ -550,19 +532,6 @@ impl Parse for FieldAttribute {
 
                 Ok(FieldAttribute::Default(Box::new(default_expr)))
             }
-            #[cfg(feature = "tracing")]
-            "tracing" => {
-                let content;
-                syn::parenthesized!(content in input);
-                let behavior = content.parse_any_ident()?;
-                tracing::FieldBehavior::from_ident(&behavior).map(|val| {
-                    FieldAttribute::Tracing(SpanContainer::new(
-                        ident.span(),
-                        Some(behavior.span()),
-                        val,
-                    ))
-                })
-            }
             _ => Err(syn::Error::new(ident.span(), "unknown attribute")),
         }
     }
@@ -579,10 +548,6 @@ pub struct FieldAttributes {
     pub arguments: HashMap<String, FieldAttributeArgument>,
     /// Only relevant for object input objects.
     pub default: Option<SpanContainer<Option<syn::Expr>>>,
-
-    // Only relevant for GraphQLObject derive and graphql_object attribute.
-    #[cfg(feature = "tracing")]
-    pub tracing: Option<tracing::FieldBehavior>,
 }
 
 impl Parse for FieldAttributes {
@@ -611,8 +576,6 @@ impl Parse for FieldAttributes {
                 FieldAttribute::Default(expr) => {
                     output.default = Some(*expr);
                 }
-                #[cfg(feature = "tracing")]
-                FieldAttribute::Tracing(tracing) => output.tracing = Some(*tracing),
             }
         }
 
@@ -673,12 +636,6 @@ pub struct GraphQLTypeDefinitionField {
     pub is_async: bool,
     pub default: Option<TokenStream>,
     pub span: Span,
-
-    // Relevant only for `#[graphql_object]` and `#[derive(GraphQLObject)]`
-    #[cfg(feature = "tracing")]
-    pub tracing_behavior: Option<tracing::FieldBehavior>,
-    #[cfg(feature = "tracing")]
-    pub instrument_attr: Option<tracing::Attr>,
 }
 
 impl syn::spanned::Spanned for GraphQLTypeDefinitionField {
@@ -720,10 +677,6 @@ pub struct GraphQLTypeDefinition {
     pub generic_scalar: bool,
     // FIXME: make this redundant.
     pub no_async: bool,
-
-    // Only relevant for GraphQL Objects.
-    #[cfg(feature = "tracing")]
-    pub tracing_rule: Option<tracing::Rule>,
 }
 
 impl GraphQLTypeDefinition {

From 3420e8f1390fcf3dc22f995937a92296c6e81ac7 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Fri, 13 Aug 2021 16:11:38 +0300
Subject: [PATCH 67/72] fmt fix

---
 juniper/src/macros/mod.rs                     |  2 +-
 juniper/src/tests/fixtures/tracing/schema.rs  |  9 ++--
 juniper_codegen/src/common/field/arg.rs       |  2 +-
 juniper_codegen/src/common/field/mod.rs       | 43 ++++++++++---------
 juniper_codegen/src/common/parse/attr.rs      |  5 +--
 juniper_codegen/src/graphql_interface/mod.rs  | 22 +++++-----
 juniper_codegen/src/graphql_object/mod.rs     | 20 ++++-----
 .../src/graphql_subscription/mod.rs           |  9 ++--
 juniper_codegen/src/tracing.rs                | 39 +++++++----------
 9 files changed, 68 insertions(+), 83 deletions(-)

diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs
index 818ecf133..b3d7433a1 100644
--- a/juniper/src/macros/mod.rs
+++ b/juniper/src/macros/mod.rs
@@ -2,4 +2,4 @@
 
 pub mod helper;
 
-mod tracing;
\ No newline at end of file
+mod tracing;
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index ac19ead7d..e684c2634 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -265,12 +265,9 @@ impl Bar {
     #[instrument(skip(skipped))]
     fn default_arg(
         &self,
-        #[graphql(default = 42)]
-        this: i32,
-        #[graphql(default = 0)]
-        another: i32,
-        #[graphql(default = 1)]
-        skipped: i32
+        #[graphql(default = 42)] this: i32,
+        #[graphql(default = 0)] another: i32,
+        #[graphql(default = 1)] skipped: i32,
     ) -> i32 {
         this + another + skipped
     }
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index db2644fb9..06eada4f5 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -385,7 +385,7 @@ impl OnMethod {
 
                 }
             }
-            Self::Context(_) | Self::Executor => quote!()
+            Self::Context(_) | Self::Executor => quote!(),
         }
     }
 
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 426c82ad3..1d6cec7cc 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -145,7 +145,7 @@ impl Parse for Attr {
                             tracing::FieldBehavior::from_ident(&behavior)?,
                         ))
                         .none_or_else(|_| err::dup_arg(&ident))?;
-                },
+                }
                 #[cfg(not(feature = "tracing"))]
                 "tracing" => {
                     return Err(err::tracing_disabled(&ident));
@@ -439,8 +439,7 @@ impl Definition {
         &self,
         scalar: &scalar::Type,
         trait_ty: Option<&syn::Type>,
-        #[cfg(feature = "tracing")]
-        traced_ty: &impl tracing::TracedType,
+        #[cfg(feature = "tracing")] traced_ty: &impl tracing::TracedType,
     ) -> Option<TokenStream> {
         if self.is_async {
             return None;
@@ -454,10 +453,12 @@ impl Definition {
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| (
-                    arg.method_resolve_field_tokens(),
-                    arg.method_resolve_arg_getter_tokens(scalar),
-                ))
+                .map(|arg| {
+                    (
+                        arg.method_resolve_field_tokens(),
+                        arg.method_resolve_arg_getter_tokens(scalar),
+                    )
+                })
                 .unzip();
 
             let rcv = self.has_receiver.then(|| {
@@ -502,8 +503,7 @@ impl Definition {
         &self,
         scalar: &scalar::Type,
         trait_ty: Option<&syn::Type>,
-        #[cfg(feature = "tracing")]
-        traced_ty: &impl tracing::TracedType,
+        #[cfg(feature = "tracing")] traced_ty: &impl tracing::TracedType,
     ) -> TokenStream {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
@@ -513,10 +513,12 @@ impl Definition {
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| (
-                    arg.method_resolve_field_tokens(),
-                    arg.method_resolve_arg_getter_tokens(scalar),
-                ))
+                .map(|arg| {
+                    (
+                        arg.method_resolve_field_tokens(),
+                        arg.method_resolve_arg_getter_tokens(scalar),
+                    )
+                })
                 .unzip();
 
             let rcv = self.has_receiver.then(|| {
@@ -564,8 +566,7 @@ impl Definition {
     pub(crate) fn method_resolve_field_into_stream_tokens(
         &self,
         scalar: &scalar::Type,
-        #[cfg(feature = "tracing")]
-        traced_ty: &impl tracing::TracedType,
+        #[cfg(feature = "tracing")] traced_ty: &impl tracing::TracedType,
     ) -> TokenStream {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
@@ -575,10 +576,12 @@ impl Definition {
                 .as_ref()
                 .unwrap()
                 .iter()
-                .map(|arg| (
-                    arg.method_resolve_field_tokens(),
-                    arg.method_resolve_arg_getter_tokens(scalar),
-                ))
+                .map(|arg| {
+                    (
+                        arg.method_resolve_field_tokens(),
+                        arg.method_resolve_arg_getter_tokens(scalar),
+                    )
+                })
                 .unzip();
 
             let rcv = self.has_receiver.then(|| {
@@ -587,7 +590,7 @@ impl Definition {
 
             (
                 quote! { Self::#ident(#rcv #( #args ),*) },
-                quote! { #( #getters )* }
+                quote! { #( #getters )* },
             )
         } else {
             ty = parse_quote! { _ };
diff --git a/juniper_codegen/src/common/parse/attr.rs b/juniper_codegen/src/common/parse/attr.rs
index 0535c92fd..e852bf289 100644
--- a/juniper_codegen/src/common/parse/attr.rs
+++ b/juniper_codegen/src/common/parse/attr.rs
@@ -57,10 +57,7 @@ pub(crate) mod err {
     #[cfg(not(feature = "tracing"))]
     #[must_use]
     pub(crate) fn tracing_disabled<S: AsSpan>(span: S) -> syn::Error {
-        syn::Error::new(
-            span.as_span(),
-            "`tracing` feature disabled",
-        )
+        syn::Error::new(span.as_span(), "`tracing` feature disabled")
     }
 
     /// Helper coercion for [`Span`] and [`Spanned`] types to use in function arguments.
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index e0d9d00c9..c3e24849c 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -19,6 +19,8 @@ use syn::{
     token,
 };
 
+#[cfg(feature = "tracing")]
+use crate::tracing;
 use crate::{
     common::{
         field, gen,
@@ -30,8 +32,6 @@ use crate::{
     },
     util::{filter_attrs, get_doc_comment, span_container::SpanContainer, RenameRule},
 };
-#[cfg(feature = "tracing")]
-use crate::tracing;
 
 /// Available arguments behind `#[graphql_interface]` attribute placed on a
 /// trait definition, when generating code for [GraphQL interface][1] type.
@@ -600,15 +600,14 @@ impl Definition {
         let ty = self.ty.ty_tokens();
         let trait_ty = self.ty.trait_ty();
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(
+        let fields_resolvers = self.fields.iter().filter_map(|f| {
+            f.method_resolve_field_tokens(
                 scalar,
                 Some(&trait_ty),
                 #[cfg(feature = "tracing")]
                 self,
-            ));
+            )
+        });
         let async_fields_panic = {
             let names = self
                 .fields
@@ -695,15 +694,14 @@ impl Definition {
         let ty = self.ty.ty_tokens();
         let trait_ty = self.ty.trait_ty();
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .map(|f| f.method_resolve_field_async_tokens(
+        let fields_resolvers = self.fields.iter().map(|f| {
+            f.method_resolve_field_async_tokens(
                 scalar,
                 Some(&trait_ty),
                 #[cfg(feature = "tracing")]
                 self,
-            ));
+            )
+        });
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         let custom_downcasts = self
diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs
index db1bb2d09..ef767cff3 100644
--- a/juniper_codegen/src/graphql_object/mod.rs
+++ b/juniper_codegen/src/graphql_object/mod.rs
@@ -500,15 +500,14 @@ impl Definition<Query> {
 
         let name = &self.name;
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .filter_map(|f| f.method_resolve_field_tokens(
+        let fields_resolvers = self.fields.iter().filter_map(|f| {
+            f.method_resolve_field_tokens(
                 scalar,
                 None,
                 #[cfg(feature = "tracing")]
-                self
-            ));
+                self,
+            )
+        });
         let async_fields_panic = {
             let names = self
                 .fields
@@ -570,15 +569,14 @@ impl Definition<Query> {
         let (impl_generics, where_clause) = self.impl_generics(true);
         let ty = &self.ty;
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .map(|f| f.method_resolve_field_async_tokens(
+        let fields_resolvers = self.fields.iter().map(|f| {
+            f.method_resolve_field_async_tokens(
                 scalar,
                 None,
                 #[cfg(feature = "tracing")]
                 self,
-            ));
+            )
+        });
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         quote! {
diff --git a/juniper_codegen/src/graphql_subscription/mod.rs b/juniper_codegen/src/graphql_subscription/mod.rs
index 1fd01c1c9..da12d35e8 100644
--- a/juniper_codegen/src/graphql_subscription/mod.rs
+++ b/juniper_codegen/src/graphql_subscription/mod.rs
@@ -96,14 +96,13 @@ impl Definition<Subscription> {
         }
         let ty = &self.ty;
 
-        let fields_resolvers = self
-            .fields
-            .iter()
-            .map(|f| f.method_resolve_field_into_stream_tokens(
+        let fields_resolvers = self.fields.iter().map(|f| {
+            f.method_resolve_field_into_stream_tokens(
                 scalar,
                 #[cfg(feature = "tracing")]
                 self,
-            ));
+            )
+        });
         let no_field_panic = field::Definition::method_resolve_field_panic_no_field_tokens(scalar);
 
         quote! {
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index baf8b35ad..eddb147ad 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -163,13 +163,11 @@ impl Rule {
     /// Constructs [`Rule`] from attribute with given name. If attribute with
     /// `attr_name` is not present then returns default [`Rule`].
     pub fn from_attrs(attr_name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
-        Ok(
-            attrs.iter()
-                .find_map(|attr| attr.path.is_ident(attr_name)
-                    .then(|| attr.parse_args()))
-                .transpose()?
-                .unwrap_or_else(Self::default)
-        )
+        Ok(attrs
+            .iter()
+            .find_map(|attr| attr.path.is_ident(attr_name).then(|| attr.parse_args()))
+            .transpose()?
+            .unwrap_or_else(Self::default))
     }
 
     /// Constructs [`Rule`] from attribute with given name, and strips it from list.
@@ -201,16 +199,14 @@ impl Parse for Rule {
             "sync" => Ok(Self::Sync),
             "skip_all" => Ok(Self::SkipAll),
             "only" => Ok(Self::Only),
-            tracing => Err(
-                syn::Error::new(
-                    ident.span(),
-                    format!(
-                        "Unknown tracing rule: {}, \
+            tracing => Err(syn::Error::new(
+                ident.span(),
+                format!(
+                    "Unknown tracing rule: {}, \
                          known values: sync, async, skip-all and complex",
-                        tracing,
-                    )
-                )
-            ),
+                    tracing,
+                ),
+            )),
         }
     }
 }
@@ -448,13 +444,10 @@ mod impls {
         }
 
         fn args(&self) -> Vec<&Self::Arg> {
-            self.arguments
-                .as_ref()
-                .map_or_else(
-                    || vec![],
-                    |args| args.iter()
-                        .filter_map(|arg| arg.as_regular())
-                        .collect())
+            self.arguments.as_ref().map_or_else(
+                || vec![],
+                |args| args.iter().filter_map(|arg| arg.as_regular()).collect(),
+            )
         }
     }
 

From 7e318038a9c375b1cd6063b08feee23134b662bb Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Sun, 15 Aug 2021 14:30:55 +0300
Subject: [PATCH 68/72] Impl sigils

---
 docs/book/content/tracing/index.md           |  27 +--
 juniper/src/tests/fixtures/tracing/mod.rs    |   7 +-
 juniper/src/tests/fixtures/tracing/schema.rs |  53 +++++-
 juniper/src/tests/tracing_tests.rs           |  84 ++++++++++
 juniper_codegen/src/common/field/mod.rs      |   8 +-
 juniper_codegen/src/tracing.rs               | 168 +++++++++++++++++--
 6 files changed, 317 insertions(+), 30 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index d4880065b..c0d4a035c 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -37,12 +37,15 @@ impl Foo {
         self.value
     }
     
-    // Here we'll record span and it will have field with name "another" and value we passed.
+    // Multiplying is much harder, so we'll trace it, recorded span will also
+    // contain value of `another`.
     fn multiply_values(&self, another: i32) -> i32 {
         self.value * another
     }
     
-    // Here we'll record span and it will have field with name "self.value"
+    // Squaring is also hard but we don't know the value we squared so we can
+    // validate the results. In this case we can use `fields` argument to pass
+    // additional fields, that also will be included in span.
     #[instrument(fields(self.value = self.value))]
     fn square_value(&self) -> i32 {
         self.value * self.value
@@ -131,10 +134,10 @@ impl User {
 }
 ```
 
-Manually setting `#[graphql(tracing(ignore))]` to avoid tracing of all, let's say for
-example synchronous field resolvers is rather inefficient when you have GraphQL
-object with too much fields. In this case you can use `tracing` attribute on
-top-level to skip tracing of specific field group or not to trace at all.
+Manually setting `#[graphql(tracing(ignore))]` to prevent tracing of all, let's
+say for example synchronous field resolvers is rather inefficient when you have 
+GraphQL object with too much fields. In this case you can use `tracing` attribute
+on top-level to trace specific group of fields or not to trace at all.
 `tracing` attribute can be used with one of the following arguments:
 `sync`, `async`, `only` or `skip_all`.
  - Use `sync` to trace only synchronous resolvers (struct fields and `fn`s).
@@ -216,10 +219,10 @@ impl Catalog {
 In example above both `filter` and `count` of `products` field will be recorded
 in produced [`Span`]. All fields will be recorded using their `fmt::Debug`
 implementation, if your field doesn't implement `fmt::Debug` you'll get compile
-time error. In this case ypu should either implement `fmt::Debug` or skip it
+time error. In this case you should either implement `fmt::Debug` or skip it
 using `#[instrument(skip(<fields to skip>))]` if you still want to record it but
-for some reason you don't want to implement `fmt::Debug` trait, consider reintroducing
-this field with `fields(field_name = some_value)` like shown bellow.
+for some reason you don't want to (or cannot) implement `fmt::Debug` trait, consider
+reintroducing this field with `fields(field_name = some_value)` like shown bellow.
 
 
 ### Example
@@ -287,9 +290,9 @@ fn self_aware() -> i32 {
 ## `#[instrument]` attribute
 
 In most aspects it mimics behavior of the original `#[instrument]` attribute
-from [tracing] crate and you could use it as a reference. With the only key
-deference you should understand, it applied implicitly to all resolvers if the
-`tracing` feature is enabled.
+from [tracing] crate including fields, sigils, and you could use it as a reference.
+The only key deference you should remember, `#[instrument]` applied implicitly to
+all resolvers if the `tracing` feature is enabled.
 
 [tracing]: https://crates.io/crates/tracing
 [`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 55861abdc..cfd196e51 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -104,12 +104,17 @@ impl TestSubscriber {
     }
 
     /// Creates [`SubscriberAssert`] used to validated constructed spans.
-    pub fn assert(self) -> SubscriberAssert {
+    pub fn assert(&self) -> SubscriberAssert {
         SubscriberAssert {
             name_to_span: HashMap::new(),
             events: self.events.borrow().clone(),
         }
     }
+
+    /// Clears shared buffer of events.
+    pub fn clear(&mut self) {
+        self.events.borrow_mut().clear()
+    }
 }
 
 impl Subscriber for TestSubscriber {
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index e684c2634..8df397a71 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -4,6 +4,7 @@
 use std::collections::HashMap;
 
 use futures::stream::{self, BoxStream, StreamExt as _};
+use tracing::instrument;
 
 use crate::{
     graphql_interface, graphql_object, graphql_subscription, tracing, Context, GraphQLObject,
@@ -26,7 +27,7 @@ impl Database {
     }
 
     /// Query mock, instrumented by [`tracing`] crate.
-    #[tracing::instrument(skip(self))]
+    #[instrument(skip(self))]
     pub fn traced_query(&self, id: i32) -> Option<String> {
         self.inner.get(&id).cloned()
     }
@@ -37,6 +38,20 @@ impl Database {
     }
 }
 
+pub struct Resolver;
+
+impl Resolver {
+    #[instrument]
+    pub fn resolve_sync() -> i32 {
+        1
+    }
+
+    #[instrument]
+    pub async fn resolve_async() -> i32 {
+        1
+    }
+}
+
 /// Query root with various queries used to test [`tracing`] compatibility.
 pub struct Query;
 
@@ -159,6 +174,28 @@ impl Query {
     fn erased_complex() -> InterfacedComplexValue {
         InterfacedComplexValue::Complex(Complex)
     }
+
+    /// Sync fn that uses instrumented function under the hood.
+    fn sub_resolver() -> i32 {
+        Resolver::resolve_sync()
+    }
+
+    /// Async fn that uses instrumented function under the hood.
+    async fn sub_async_resolver() -> i32 {
+        Resolver::resolve_async().await
+    }
+
+    /// Fn that has custom field marked with debug sigil (`?`).
+    #[instrument(fields(sigil = ?Sigil))]
+    fn debug_sigil() -> i32 {
+        1
+    }
+
+    /// Fn that has custom field marked with display sigil (`%`).
+    #[instrument(fields(sigil = %Sigil))]
+    fn display_sigil() -> i32 {
+        1
+    }
 }
 
 /// Subscriptions root with various queries used to test [`tracing`] compatibility.
@@ -534,3 +571,17 @@ trait InterfacedComplex {
     #[graphql(tracing(only))]
     async fn async_fn(&self) -> i32;
 }
+
+struct Sigil;
+
+impl std::fmt::Debug for Sigil {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        fmt.write_str("Debug Sigil")
+    }
+}
+
+impl std::fmt::Display for Sigil {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        fmt.write_str("Display Sigil")
+    }
+}
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 0a7968478..0772e301e 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -570,3 +570,87 @@ async fn subscription_tracing() {
         )
         .close_exited("Subscriptions.barSub");
 }
+
+#[tokio::test]
+async fn sub_resolvers() {
+    let doc_async = r#"
+        {
+            subResolver
+            subAsyncResolver
+        }
+        "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (mut handle, _guard) = init_tracer();
+
+    let res = juniper::execute(doc_async, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query_async")
+        .enter_new_span("Query.subResolver")
+        // Sync sub resolver in async context
+        .simple_span("resolve_sync")
+        .close_exited("Query.subResolver")
+        .enter_new_span("Query.subAsyncResolver")
+        // Async sub resolver in async context
+        .simple_span("resolve_async")
+        .close_exited("Query.subAsyncResolver")
+        .close_exited("execute_validated_query_async")
+        .close_exited("execute");
+
+    handle.clear();
+
+    let doc_sync = r#"
+        {
+            subResolver
+        }
+        "#;
+
+    let res = juniper::execute_sync(doc_sync, None, &schema, &Variables::new(), &database);
+    assert!(res.is_ok(), "Should be ok");
+
+    handle
+        .assert()
+        .enter_new_span("execute_sync")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .enter_new_span("Query.subResolver")
+        // Sync sub resolver in sync context
+        .simple_span("resolve_sync")
+        .close_exited("Query.subResolver")
+        .close_exited("execute_validated_query")
+        .close_exited("execute_sync");
+}
+
+#[tokio::test]
+async fn tracing_compat_sigil() {
+    let doc_async = r#"
+        {
+            debugSigil
+            displaySigil
+        }
+        "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (handle, _guard) = init_tracer();
+
+    let res = juniper::execute(doc_async, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query_async")
+        .simple_span(&"Query.debugSigil".with_field("sigil", "Debug Sigil"))
+        .simple_span(&"Query.displaySigil".with_field("sigil", "Display Sigil"))
+        .close_exited("execute_validated_query_async")
+        .close_exited("execute");
+}
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 1d6cec7cc..282d5fc34 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -536,7 +536,13 @@ impl Definition {
             (quote! { &self.#ident }, quote!())
         };
         if !self.is_async {
-            fut = quote! { ::juniper::futures::future::ready(#fut) };
+            let trace_sync = if_tracing_enabled!(tracing::sync_tokens(traced_ty, self));
+            fut = quote! {
+                ::juniper::futures::future::ready({
+                    #trace_sync
+                    #fut
+                })
+            };
         }
 
         let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index eddb147ad..1e75fc0cb 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -2,11 +2,11 @@ use std::{collections::HashMap, mem};
 
 use proc_macro2::TokenStream;
 use proc_macro_error::abort;
-use quote::quote;
+use quote::{quote, ToTokens, TokenStreamExt as _};
 use syn::{
     parse::{Parse, ParseStream},
     spanned::Spanned as _,
-    token,
+    Token,
 };
 
 use crate::common::parse::ParseBufferExt as _;
@@ -14,7 +14,7 @@ use crate::common::parse::ParseBufferExt as _;
 pub const ATTR_NAME: &'static str = "instrument";
 
 /// `#[instrument]` attribute placed on field resolver.
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
 pub struct Attr {
     /// Optional span rename, if `None` method name should be used instead.
     name: Option<syn::LitStr>,
@@ -29,7 +29,7 @@ pub struct Attr {
     skip: HashMap<String, syn::Ident>,
 
     /// Custom fields.
-    fields: Vec<syn::ExprAssign>,
+    fields: Vec<Field>,
 }
 
 impl Attr {
@@ -100,15 +100,15 @@ impl Parse for Attr {
 
             match name.to_string().as_str() {
                 "name" => {
-                    input.parse::<token::Eq>()?;
+                    input.parse::<Token![=]>()?;
                     attr.name = Some(input.parse()?);
                 }
                 "level" => {
-                    input.parse::<token::Eq>()?;
+                    input.parse::<Token![=]>()?;
                     attr.level = Some(input.parse()?);
                 }
                 "target" => {
-                    input.parse::<token::Eq>()?;
+                    input.parse::<Token![=]>()?;
                     attr.target = Some(input.parse()?);
                 }
                 "skip" => {
@@ -118,7 +118,7 @@ impl Parse for Attr {
                         let field: syn::Ident = skipped_fields.parse()?;
                         attr.skip.insert(field.to_string(), field);
 
-                        skipped_fields.parse::<token::Comma>().ok();
+                        skipped_fields.parse::<Token![,]>().ok();
                     }
                 }
                 "fields" => {
@@ -127,19 +127,161 @@ impl Parse for Attr {
                     while !fields.is_empty() {
                         attr.fields.push(fields.parse()?);
 
-                        fields.parse::<token::Comma>().ok();
+                        fields.parse::<Token![,]>().ok();
                     }
                 }
                 _ => return Err(syn::Error::new(name.span(), "unknown attribute")),
             }
 
             // Discard trailing comma.
-            input.parse::<token::Comma>().ok();
+            input.parse::<Token![,]>().ok();
         }
         Ok(attr)
     }
 }
 
+/// Custom field that should be recorded in span, explicitly defined by user.
+#[derive(Clone, Debug)]
+pub struct Field {
+    /// Left part of this [`Field`], represents name of recorded field.
+    left: FieldName,
+
+    /// Eq sign between left and right parts.
+    eq_sign: Option<Token![=]>,
+
+    /// Sigil that determines how to display this [`Field`]
+    sigil: Option<Sigil>,
+
+    /// Right part of this [`Field`] represents value that should be recorded.
+    right: Option<syn::Expr>,
+}
+
+impl Parse for Field {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        // If sigil is present at this point we're dealing with `%self.field`
+        // so there should be nothing after `left`.
+        let sigil = Sigil::try_parse(input);
+        let left = input.parse()?;
+
+        if sigil.is_none() && input.lookahead1().peek(Token![=]) {
+            Ok(Self {
+                left,
+                eq_sign: Some(input.parse()?),
+                sigil: Sigil::try_parse(input),
+                right: Some(input.parse()?),
+            })
+        } else {
+            Ok(Self {
+                left,
+                sigil,
+                eq_sign: None,
+                right: None,
+            })
+        }
+    }
+}
+
+impl ToTokens for Field {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        if self.right.is_none() {
+            // If we don't have right part we're dealing with field of type `var.name`.
+            let sigil = &self.sigil;
+            let left = &self.left;
+            tokens.append_all(quote! { #sigil #left })
+        } else {
+            // Otherwise we're dealing with field of type `var.name = other.value`.
+            let left = &self.left;
+            let eq_sign = &self.eq_sign;
+            let sigil = &self.sigil;
+            let right = &self.right;
+            tokens.append_all(quote! { #left #eq_sign #sigil #right })
+        }
+    }
+}
+
+/// Possible values of [`Field`] names.
+#[derive(Clone, Debug)]
+pub enum FieldName {
+    /// Idents divided by dots, `var.name.and.even.more`
+    ExprField(syn::punctuated::Punctuated<syn::Ident, syn::Token![.]>),
+
+    /// Single ident like `self`, `var_name`.
+    Ident(syn::Ident),
+
+    /// String literal.
+    LitStr(syn::LitStr),
+}
+
+impl ToTokens for FieldName {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        tokens.append_all(match self {
+            Self::Ident(ident) => quote!(#ident),
+            Self::LitStr(lit) => quote!(#lit),
+            Self::ExprField(expr) => quote!(#expr),
+        })
+    }
+}
+
+impl Parse for FieldName {
+    fn parse(input: ParseStream) -> syn::Result<Self> {
+        if input.peek(syn::LitStr) {
+            return Ok(Self::LitStr(input.parse()?));
+        }
+
+        let ident = input.parse_any_ident()?;
+
+        Ok(if input.peek(Token![.]) {
+            let mut idents = syn::punctuated::Punctuated::new();
+            idents.push_value(ident);
+            while input.peek(Token![.]) {
+                idents.push_punct(input.parse::<Token![.]>()?);
+                idents.push_value(input.parse_any_ident()?);
+            }
+            Self::ExprField(idents)
+        } else {
+            Self::Ident(ident)
+        })
+    }
+}
+
+/// Short markers that used to enforce certain formatting on custom [`Field`].
+#[derive(Clone, Debug)]
+pub enum Sigil {
+    /// [`Field`] should be formatted as [`fmt::Debug`].
+    ///
+    /// [`fmt::Debug`]: std::fmt::Debug
+    Debug(Token![?]),
+
+    /// [`Field`] should be formatted as [`fmt::Display`].
+    ///
+    /// [`fmt::Display`]: std::fmt::Display
+    Display(Token![%]),
+}
+
+impl ToTokens for Sigil {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Self::Debug(t) => tokens.append_all(quote!(#t)),
+            Self::Display(t) => tokens.append_all(quote!(#t)),
+        }
+    }
+}
+
+impl Sigil {
+    /// Tries to parse [`Sigil`] from the given `stream`.
+    fn try_parse(stream: ParseStream) -> Option<Self> {
+        let lookahead = stream.lookahead1();
+
+        if lookahead.peek(syn::Token![?]) {
+            return Some(Self::Debug(stream.parse().unwrap()));
+        }
+        if lookahead.peek(syn::Token![%]) {
+            return Some(Self::Display(stream.parse().unwrap()));
+        }
+        None
+    }
+}
+
 /// The different possible groups of fields to trace.
 #[derive(Copy, Clone, Debug)]
 pub enum Rule {
@@ -328,11 +470,7 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     });
 
     let args: Vec<_> = if let Some(tracing) = field.instrument() {
-        let additional_fields = tracing.fields.iter().map(|f| {
-            let name = &f.left;
-            let right = &f.right;
-            quote!(#name = ::juniper::tracing::field::debug(#right))
-        });
+        let additional_fields = tracing.fields.iter().map(|f| quote!(#f));
 
         args.chain(additional_fields).collect()
     } else {

From ae02db7caa1565b29ef62a707165a24b50cde3e5 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Mon, 16 Aug 2021 08:30:22 +0300
Subject: [PATCH 69/72] Impl field::Empty

---
 docs/book/content/tracing/index.md           | 55 +++++++++++++++++++-
 juniper/src/tests/fixtures/tracing/mod.rs    | 22 +++++---
 juniper/src/tests/fixtures/tracing/schema.rs |  9 +++-
 juniper/src/tests/tracing_tests.rs           | 37 +++++++++++++
 juniper_codegen/src/common/field/arg.rs      | 14 +++++
 5 files changed, 128 insertions(+), 9 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index c0d4a035c..c01d02afe 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -236,7 +236,7 @@ reintroducing this field with `fields(field_name = some_value)` like shown bello
 
 #[derive(Clone, juniper::GraphQLInputObject)]
 struct NonDebug {
-    important_field: String,
+    important_field: i32,
 }
 
 # #[graphql_object]
@@ -287,6 +287,58 @@ fn self_aware() -> i32 {
 # }
 ```
 
+## `Empty` fields
+
+Field names may also be specified without values. Doing so will result in an
+empty field whose value may be recorded later within the function body. This
+can be done by adding field with value `Empty` (see [`field::Empty`]) and passing
+[`Span`] to the resolver function.
+
+### Example
+
+```rust
+# extern crate juniper;
+# use juniper::graphql_object;
+#
+# fn main() {}
+#
+use tracing::field; // alternatively you can use `juniper::tracing::field`
+
+#[derive(Debug)]
+struct Foo;
+
+struct Query {
+    data: i32,
+}
+
+# #[graphql_object]
+# impl Query {
+#[instrument(fields(later = field::Empty))]
+async fn empty_field(tracing_span: tracing::Span) -> i32 {
+    // Use `record("<field_name>", &value)`.
+    tracing_span.record("later", &"see ya later!");
+    // resolver code
+#   unimplemented!()
+}
+#[instrument(fields(msg = "Everything is OK."))]
+async fn override_field(tracing_span: tracing::Span) -> i32 {
+    // We can override `msg` with the same syntax as we recorded `later` in
+    // example above. In fact `Empty` field is a special case of overriding.
+    tracing_span.record("msg", &"Everything is Perfect!");
+    // In cases when you want to record a non-standard value to span you may
+    // use `field::debug(...)` and `field::display(...)`, to set proper formatting.
+    tracing_span.record("msg", &field::debug(&Foo));
+    // Doing `tracing_span.record("msg", Foo)` will result in compile error.
+#   unimplemented!()
+}
+# }
+```
+
+**Note:** To avoid collision with variable names [`Span`] should bee passed with
+one of the following variable names: `tracing_span`, `_span` and `_tracing_span`.
+Also keep in mind that if `Empty` field wasn't recorded it won't be included in
+span.
+
 ## `#[instrument]` attribute
 
 In most aspects it mimics behavior of the original `#[instrument]` attribute
@@ -297,3 +349,4 @@ all resolvers if the `tracing` feature is enabled.
 [tracing]: https://crates.io/crates/tracing
 [`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
 [`Span`]: https://docs.rs/tracing/0.1.26/tracing/struct.Span.html
+[`field::Empty`]: https://docs.rs/tracing/0.1.26/tracing/field/struct.Empty.html
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index cfd196e51..97f6ffa97 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -41,9 +41,6 @@ enum SubscriberEvent {
     /// `exit` method.
     Exit(span::Id),
 
-    /// `clone_span` method.
-    CloneSpan(span::Id),
-
     /// `try_close` method.
     TryClose(span::Id),
 
@@ -141,7 +138,21 @@ impl Subscriber for TestSubscriber {
         id
     }
 
-    fn record(&self, _: &span::Id, _: &Record<'_>) {}
+    fn record(&self, id: &span::Id, record: &Record<'_>) {
+        self.events.borrow_mut().iter_mut().for_each(|ev| match ev {
+            SubscriberEvent::NewSpan(s) if &s.id == id => {
+                let mut visit = Visitor(HashMap::new());
+                record.record(&mut visit);
+                for (key, value) in visit.0 {
+                    assert!(
+                        s.fields.insert(key, value).is_none(),
+                        "Cannot override non-existing field",
+                    )
+                }
+            }
+            _ => {}
+        })
+    }
 
     fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
 
@@ -164,9 +175,6 @@ impl Subscriber for TestSubscriber {
     }
 
     fn clone_span(&self, id: &span::Id) -> span::Id {
-        self.events
-            .borrow_mut()
-            .push(SubscriberEvent::CloneSpan(id.clone()));
         id.clone()
     }
 
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 8df397a71..658b5dc43 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -4,7 +4,7 @@
 use std::collections::HashMap;
 
 use futures::stream::{self, BoxStream, StreamExt as _};
-use tracing::instrument;
+use tracing::{field, instrument};
 
 use crate::{
     graphql_interface, graphql_object, graphql_subscription, tracing, Context, GraphQLObject,
@@ -196,6 +196,13 @@ impl Query {
     fn display_sigil() -> i32 {
         1
     }
+
+    /// Fn that has custom field that's can be recorded later.
+    #[instrument(fields(magic = field::Empty))]
+    async fn empty_field(tracing_span: tracing::Span) -> i32 {
+        tracing_span.record("magic", &"really magic");
+        1
+    }
 }
 
 /// Subscriptions root with various queries used to test [`tracing`] compatibility.
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 0772e301e..55248a254 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -654,3 +654,40 @@ async fn tracing_compat_sigil() {
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
+
+#[tokio::test]
+async fn tracing_compat_empty_field() {
+    let doc = r#"
+    {
+        emptyField
+    }
+    "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (handle, _guard) = init_tracer();
+
+    let res = juniper::execute(doc, None, &schema, &Variables::new(), &database).await;
+    assert!(res.is_ok(), "Should be ok");
+
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query_async")
+        .enter_new_span(
+            &"Query.emptyField"
+                .with_field("magic", "\"really magic\"")
+                .with_strict_fields(true),
+        )
+        // This happens because we're passing owned copy of `Span` in
+        // `empty_field`, and `Span` attempts to close itself because
+        // of `Drop` implementation.
+        .try_close("Query.emptyField")
+        // At this point we resolved `Query.emptyField` and all of it's childs
+        // (which don't exists, in this test case) and it's now safe to exit
+        // and close it.
+        .close_exited("Query.emptyField")
+        .close_exited("execute_validated_query_async")
+        .close_exited("execute");
+}
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index 06eada4f5..9e10eae2d 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -277,6 +277,13 @@ pub(crate) enum OnMethod {
     /// [`Executor`]: juniper::Executor
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     Executor,
+
+    /// [`Span`] passed into a [GraphQL field][2] resolving method.
+    ///
+    /// [`Span`]: tracing::Span
+    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    #[cfg(feature = "tracing")]
+    Span,
 }
 
 impl OnMethod {
@@ -362,6 +369,9 @@ impl OnMethod {
             },
 
             Self::Executor => quote! { &executor },
+
+            #[cfg(feature = "tracing")]
+            Self::Span => quote! { _tracing_span.clone() },
         }
     }
 
@@ -386,6 +396,8 @@ impl OnMethod {
                 }
             }
             Self::Context(_) | Self::Executor => quote!(),
+            #[cfg(feature = "tracing")]
+            Self::Span => quote!(),
         }
     }
 
@@ -423,6 +435,8 @@ impl OnMethod {
                     Some(Self::Context(argument.ty.unreferenced().clone()))
                 }
                 "executor" | "_executor" => Some(Self::Executor),
+                #[cfg(feature = "tracing")]
+                "tracing_span" | "_span" | "_tracing_span" => Some(Self::Span),
                 _ => None,
             };
             if arg.is_some() {

From e69f2610337e9538eb46fc04af579ab446d4ae1f Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Mon, 16 Aug 2021 15:09:49 +0300
Subject: [PATCH 70/72] Improve subscription tracing [run ci]

---
 docs/book/content/tracing/index.md           | 116 +++++++++---
 juniper/src/lib.rs                           |   3 +
 juniper/src/tests/fixtures/tracing/mod.rs    |  31 ++-
 juniper/src/tests/fixtures/tracing/schema.rs |  54 +++++-
 juniper/src/tests/tracing_tests.rs           | 189 ++++++++++++++++++-
 juniper_codegen/src/common/field/arg.rs      |  14 --
 juniper_codegen/src/common/field/mod.rs      |  37 ++--
 juniper_codegen/src/graphql_object/derive.rs |   4 +-
 juniper_codegen/src/tracing.rs               | 144 ++++++++++++--
 9 files changed, 510 insertions(+), 82 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index c01d02afe..7e82046e1 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -99,6 +99,15 @@ async fn main() {
 }
 ```
 
+# `#[instrument]` attribute
+
+Juniper has it's own `#[instrument]` attribute, that can only be used with
+GraphQL Objects and Interfaces. In most aspects it mimics behavior of the original
+`#[instrument]` attribute from [tracing] crate including fields, sigils, and you
+could use it as a reference. First and the only key deference you should keep in
+mind, `#[instrument]` applied implicitly to **all** resolvers if the `tracing`
+feature is enabled.
+
 ## Skipping field resolvers
 
 In certain scenarios you may want to skip tracing of some fields because it too
@@ -134,7 +143,7 @@ impl User {
 }
 ```
 
-Manually setting `#[graphql(tracing(ignore))]` to prevent tracing of all, let's
+Manually setting `#[graphql(tracing(ignore))]` to skip tracing of all, let's
 say for example synchronous field resolvers is rather inefficient when you have 
 GraphQL object with too much fields. In this case you can use `tracing` attribute
 on top-level to trace specific group of fields or not to trace at all.
@@ -291,8 +300,8 @@ fn self_aware() -> i32 {
 
 Field names may also be specified without values. Doing so will result in an
 empty field whose value may be recorded later within the function body. This
-can be done by adding field with value `Empty` (see [`field::Empty`]) and passing
-[`Span`] to the resolver function.
+can be done by adding field with value `Empty` (see [`field::Empty`]) and 
+accessing `tracing::Span::current()` within resolver body.
 
 ### Example
 
@@ -307,44 +316,109 @@ use tracing::field; // alternatively you can use `juniper::tracing::field`
 #[derive(Debug)]
 struct Foo;
 
-struct Query {
-    data: i32,
-}
+struct Query;
 
 # #[graphql_object]
 # impl Query {
 #[instrument(fields(later = field::Empty))]
-async fn empty_field(tracing_span: tracing::Span) -> i32 {
-    // Use `record("<field_name>", &value)`.
-    tracing_span.record("later", &"see ya later!");
+async fn empty_field() -> i32 {
+    // Use `record("<field_name>", &value)` to record value into empty field.
+    tracing::Span::current().record("later", &"see ya later!");
     // resolver code
 #   unimplemented!()
 }
+
 #[instrument(fields(msg = "Everything is OK."))]
-async fn override_field(tracing_span: tracing::Span) -> i32 {
+async fn override_field() -> i32 {
     // We can override `msg` with the same syntax as we recorded `later` in
     // example above. In fact `Empty` field is a special case of overriding.
-    tracing_span.record("msg", &"Everything is Perfect!");
+    tracing::Span::current().record("msg", &"Everything is Perfect!");
     // In cases when you want to record a non-standard value to span you may
     // use `field::debug(...)` and `field::display(...)`, to set proper formatting.
-    tracing_span.record("msg", &field::debug(&Foo));
+    tracing::Span::current().record("msg", &field::debug(&Foo));
     // Doing `tracing_span.record("msg", Foo)` will result in compile error.
 #   unimplemented!()
 }
 # }
 ```
 
-**Note:** To avoid collision with variable names [`Span`] should bee passed with
-one of the following variable names: `tracing_span`, `_span` and `_tracing_span`.
-Also keep in mind that if `Empty` field wasn't recorded it won't be included in
-span.
+## Error handling
+
+When resolver returns `Result<T, E>`, you can add `err` argument to `#[instrument]`
+attribute, doing so you will create empty field `err` that will be recorded if your
+resolver function will return `Result::Err(...)`. If it used with any type other than
+`Result<T, E>` it will result in compilation error.
+
+Additionally you could do this manually using the `Empty` field and manual recording.
+This is more versatile solution and as a tradeoff, it applies additional responsibility
+on user code so should be used with care.
+
+### Example
+
+```rust
+# extern crate juniper;
+# use std::fmt;
+# use juniper::graphql_object;
+#
+# fn main() {}
+#
+# struct Query;
+use tracing::field;
+
+#[derive(Debug)]
+struct Foo;
+
+struct MyError;
+
+impl fmt::Display for MyError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Definitely not an error, trust me")
+    }
+}
 
-## `#[instrument]` attribute
+# #[graphql_object]
+# impl Query {
+// Under the hood it will create `Empty` field with name "err", and will record
+// it within `resolve_field` or `resolve_field_async`.
+#[instrument(err)]
+async fn my_calculation() -> Result<i32, MyError> {
+    Err(MyError)
+}
+
+// Here we have manually created `Empty` field with name "err".
+#[instrument(fields(err = field::Empty))]
+async fn conditional_error() -> Result<i32, MyError> {
+    let res = Err(MyError);
+#   let condition = false;
+    // At this point we manually check whether result is error or not and if
+    // condition is met and only then record error.
+    if let Err(err) = &res && condition {
+        tracing::Span::current().record("err", &field::display(err));
+    }
+    res
+}
+# }
+```
 
-In most aspects it mimics behavior of the original `#[instrument]` attribute
-from [tracing] crate including fields, sigils, and you could use it as a reference.
-The only key deference you should remember, `#[instrument]` applied implicitly to
-all resolvers if the `tracing` feature is enabled.
+## Subscriptions and tracing
+
+Subscriptions a little bit harder to trace than futures and other resolvers,
+they can (and in most cases will) produce sequences of results and resolving
+will produce multiple almost identical groups of spans, to address this issue
+Juniper has two layers of [`Span`]s, first a global one, represents whole
+subscription from start and until last result (or first Error). And local
+second layer, it represents **field resolution process** of a single value
+returned by subscription. It won't capture how object is queried, but stream
+**may** do this under some conditions.
+
+For example you have subscription that queries database once a second for each
+subscriber, in this case you can easily trace every individual step. But once
+we introduce more users following the best practices we should perform this
+queries in some sort of a batch. Juniper offers coordinators which perform some
+magic under the hood to manage all subscriptions. For example scheduling multiple
+subscription to single database query. This raises a question, which span should
+be picked as parent for this query, and answer is implementation dependent, so
+should be handled manually. 
 
 [tracing]: https://crates.io/crates/tracing
 [`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs
index 04fc435a8..2896f931b 100644
--- a/juniper/src/lib.rs
+++ b/juniper/src/lib.rs
@@ -148,6 +148,9 @@ pub mod tests;
 #[cfg(test)]
 mod executor_tests;
 
+// Needs to be public because macros use it.
+pub use crate::util::to_camel_case;
+
 use crate::{
     executor::{execute_validated_query, get_operation},
     introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 97f6ffa97..6d0cfffc6 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -6,18 +6,19 @@ pub mod schema;
 
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
-use tracing_core::{span, Subscriber};
+// use tracing_core::Subscriber;
 
 use crate::tracing::{
     field::{Field, Visit},
-    span::{Attributes, Record},
-    Event, Level, Metadata,
+    span::{self, Attributes, Record},
+    Event, Level, Metadata, Subscriber,
 };
 
 /// Information about `tracing` span recorded within tests.
 #[derive(Clone, Debug)]
 struct TestSpan {
     id: span::Id,
+    parent: Option<span::Id>,
     fields: HashMap<String, String>,
     metadata: &'static Metadata<'static>,
 }
@@ -83,6 +84,10 @@ pub struct TestSubscriber {
     /// Counter used to create unique [`span::Id`]s.
     counter: Rc<RefCell<u64>>,
 
+    current_span: Rc<RefCell<Option<span::Id>>>,
+
+    spans: Rc<RefCell<HashMap<span::Id, TestSpan>>>,
+
     /// Log of method calls to this subscriber.
     events: Rc<RefCell<Vec<SubscriberEvent>>>,
 }
@@ -96,6 +101,8 @@ impl TestSubscriber {
     pub fn new() -> Self {
         TestSubscriber {
             counter: Rc::new(RefCell::new(1)),
+            current_span: Rc::new(RefCell::new(None)),
+            spans: Rc::new(RefCell::new(HashMap::new())),
             events: Rc::new(RefCell::new(Vec::new())),
         }
     }
@@ -129,9 +136,13 @@ impl Subscriber for TestSubscriber {
         let id = span::Id::from_u64(id);
         let test_span = TestSpan {
             id: id.clone(),
+            parent: self.current_span.borrow().clone(),
             metadata: attrs.metadata(),
             fields: visitor.0,
         };
+        self.spans
+            .borrow_mut()
+            .insert(id.clone(), test_span.clone());
         self.events
             .borrow_mut()
             .push(SubscriberEvent::NewSpan(test_span));
@@ -163,12 +174,14 @@ impl Subscriber for TestSubscriber {
     }
 
     fn enter(&self, id: &span::Id) {
+        *self.current_span.borrow_mut() = Some(id.clone());
         self.events
             .borrow_mut()
             .push(SubscriberEvent::Enter(id.clone()))
     }
 
     fn exit(&self, id: &span::Id) {
+        *self.current_span.borrow_mut() = self.spans.borrow().get(id).unwrap().parent.clone();
         self.events
             .borrow_mut()
             .push(SubscriberEvent::Exit(id.clone()))
@@ -182,6 +195,18 @@ impl Subscriber for TestSubscriber {
         self.events.borrow_mut().push(SubscriberEvent::TryClose(id));
         false
     }
+
+    fn current_span(&self) -> tracing_core::span::Current {
+        let current_id = self.current_span.borrow();
+
+        current_id
+            .as_ref()
+            .map(|id| {
+                let span = self.spans.borrow().get(&id).unwrap().clone();
+                tracing_core::span::Current::new(span.id, span.metadata)
+            })
+            .unwrap_or_else(tracing_core::span::Current::none)
+    }
 }
 
 /// Wrapper representing span tree received from [`TestSubscriber`].
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index 658b5dc43..c0889dddc 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -1,13 +1,14 @@
 //! Schema that contains all the necessities to test integration with
 //! [`tracing`] crate.
 
-use std::collections::HashMap;
+use std::{collections::HashMap, fmt};
 
 use futures::stream::{self, BoxStream, StreamExt as _};
-use tracing::{field, instrument};
 
 use crate::{
-    graphql_interface, graphql_object, graphql_subscription, tracing, Context, GraphQLObject,
+    graphql_interface, graphql_object, graphql_subscription,
+    tracing::{self, field, instrument},
+    Context, FieldError, GraphQLObject,
 };
 
 /// Test database.
@@ -199,10 +200,45 @@ impl Query {
 
     /// Fn that has custom field that's can be recorded later.
     #[instrument(fields(magic = field::Empty))]
-    async fn empty_field(tracing_span: tracing::Span) -> i32 {
-        tracing_span.record("magic", &"really magic");
+    async fn empty_field() -> i32 {
+        tracing::Span::current().record("magic", &"really magic");
         1
     }
+
+    /// Async fn that will record it's error.
+    #[instrument(err)]
+    async fn record_err_async(should_err: bool) -> Result<i32, Error> {
+        if should_err {
+            Err(Error)
+        } else {
+            Ok(1)
+        }
+    }
+
+    /// Async fn that will record it's error.
+    #[instrument(err)]
+    fn record_err_sync(should_err: bool) -> Result<i32, Error> {
+        if should_err {
+            Err(Error)
+        } else {
+            Ok(1)
+        }
+    }
+}
+
+/// Custom error used to test `#[instrument(err)]` functionality.
+pub struct Error;
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Definitely not an error, trust me")
+    }
+}
+
+impl<S> juniper::IntoFieldError<S> for Error {
+    fn into_field_error(self) -> FieldError<S> {
+        FieldError::new(self, juniper::Value::Null)
+    }
 }
 
 /// Subscriptions root with various queries used to test [`tracing`] compatibility.
@@ -215,6 +251,14 @@ impl Subscriptions {
 
         stream::iter(items).boxed()
     }
+
+    /// Subscription that emits `Result<T, E>`.
+    #[instrument(err)]
+    async fn err_sub() -> BoxStream<'static, Result<i32, Error>> {
+        let items = [Err(Error)];
+
+        stream::iter(items).boxed()
+    }
 }
 
 macro_rules! build_impl {
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 55248a254..458b6c0ab 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -538,7 +538,6 @@ async fn subscription_tracing() {
         }
     }
 
-    // Required argument
     handle
         .assert()
         .enter_new_span("resolve_into_stream")
@@ -553,6 +552,7 @@ async fn subscription_tracing() {
         .close_exited("resolve_validated_subscription")
         .close_exited("resolve_into_stream")
         .enter("Subscriptions.barSub")
+        .enter_new_span("next")
         .simple_span(&"Bar.id".with_field("self.id", "11"))
         .simple_span(
             &"Bar.defaultArg"
@@ -560,7 +560,9 @@ async fn subscription_tracing() {
                 .with_field("another", "-1")
                 .with_strict_fields(true),
         )
+        .close_exited("next")
         .re_enter("Subscriptions.barSub")
+        .enter_new_span("next")
         .simple_span(&"Bar.id".with_field("self.id", "12"))
         .simple_span(
             &"Bar.defaultArg"
@@ -568,6 +570,7 @@ async fn subscription_tracing() {
                 .with_field("another", "-1")
                 .with_strict_fields(true),
         )
+        .close_exited("next")
         .close_exited("Subscriptions.barSub");
 }
 
@@ -684,10 +687,188 @@ async fn tracing_compat_empty_field() {
         // `empty_field`, and `Span` attempts to close itself because
         // of `Drop` implementation.
         .try_close("Query.emptyField")
-        // At this point we resolved `Query.emptyField` and all of it's childs
-        // (which don't exists, in this test case) and it's now safe to exit
-        // and close it.
+        // At this point we resolved `Query.emptyField` and all of it's child
+        // spans (which don't exists, in this test case) and it's now safe to
+        // exit and close it.
         .close_exited("Query.emptyField")
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
+
+#[tokio::test]
+async fn tracing_compat_err_recording() {
+    let doc_sync = r#"
+    {
+        ok: recordErrSync(shouldErr: false)
+        err: recordErrSync(shouldErr: true)
+    }
+    "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (mut handle, _guard) = init_tracer();
+
+    let _ = juniper::execute(doc_sync, None, &schema, &Variables::new(), &database).await;
+
+    // Sync in async context.
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query_async")
+        .simple_span(
+            &"Query.recordErrSync"
+                .with_field("shouldErr", "false")
+                // Should be exactly 1 field because err was not recorded.
+                .with_strict_fields(true),
+        )
+        .enter_new_span(
+            &"Query.recordErrSync"
+                .with_field("shouldErr", "true")
+                .with_field("err", "Definitely not an error, trust me")
+                // Here should be present both fields because error was returned.
+                .with_strict_fields(true),
+        )
+        .try_close("Query.recordErrSync")
+        .close_exited("Query.recordErrSync")
+        .close_exited("execute_validated_query_async")
+        .close_exited("execute");
+
+    handle.clear();
+
+    let _ = juniper::execute_sync(doc_sync, None, &schema, &Variables::new(), &database);
+
+    // Sync in sync context
+    handle
+        .assert()
+        .enter_new_span("execute_sync")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query")
+        .simple_span(
+            &"Query.recordErrSync"
+                .with_field("shouldErr", "false")
+                // Should be exactly 1 field because err was not recorded.
+                .with_strict_fields(true),
+        )
+        .enter_new_span(
+            &"Query.recordErrSync"
+                .with_field("shouldErr", "true")
+                .with_field("err", "Definitely not an error, trust me")
+                // Here should be present both fields because error was returned.
+                .with_strict_fields(true),
+        )
+        .try_close("Query.recordErrSync")
+        .close_exited("Query.recordErrSync")
+        .close_exited("execute_validated_query")
+        .close_exited("execute_sync");
+
+    handle.clear();
+
+    let doc_async = r#"
+    {
+        ok: recordErrAsync(shouldErr: false)
+        err: recordErrAsync(shouldErr: true)
+    }
+    "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (handle, _guard) = init_tracer();
+
+    let _ = juniper::execute(doc_async, None, &schema, &Variables::new(), &database).await;
+
+    // Async in async context
+    handle
+        .assert()
+        .enter_new_span("execute")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("execute_validated_query_async")
+        .simple_span(
+            &"Query.recordErrAsync"
+                .with_field("shouldErr", "false")
+                // Should be exactly 1 field because err was not recorded.
+                .with_strict_fields(true),
+        )
+        .enter_new_span(
+            &"Query.recordErrAsync"
+                .with_field("shouldErr", "true")
+                .with_field("err", "Definitely not an error, trust me")
+                // Here should be present both fields because error was returned.
+                .with_strict_fields(true),
+        )
+        .try_close("Query.recordErrAsync")
+        .close_exited("Query.recordErrAsync")
+        .close_exited("execute_validated_query_async")
+        .close_exited("execute");
+}
+
+#[tokio::test]
+async fn tracing_compat_err_on_subscriptions() {
+    let doc = r#"
+        subscription {
+            errSub
+        }
+        "#;
+    let schema = init_schema();
+    let database = Database::new();
+    let (handle, _guard) = init_tracer();
+
+    let request = crate::http::GraphQLRequest::new(doc.to_owned(), None, None);
+
+    let response = crate::http::resolve_into_stream(&request, &schema, &database).await;
+
+    assert!(response.is_ok());
+
+    let (values, errors) = response.unwrap();
+
+    assert_eq!(errors.len(), 0, "Should return no errors");
+
+    // cannot compare with `assert_eq` because
+    // stream does not implement Debug
+    let response_value_object = match values {
+        juniper::Value::Object(o) => Some(o),
+        _ => None,
+    };
+
+    assert!(response_value_object.is_some());
+
+    let response_returned_object = response_value_object.unwrap();
+
+    let fields = response_returned_object.into_iter();
+
+    let mut names = vec![];
+    let mut collected_values = vec![];
+
+    for (name, stream_val) in fields {
+        names.push(name.clone());
+
+        // since macro returns Value::Scalar(iterator) every time,
+        // other variants may be skipped
+        match stream_val {
+            juniper::Value::Scalar(stream) => {
+                let collected = stream.collect::<Vec<_>>().await;
+                collected_values.push(collected);
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    handle
+        .assert()
+        .enter_new_span("resolve_into_stream")
+        .simple_span("validate_document")
+        .simple_span("validate_input_values")
+        .enter_new_span("resolve_validated_subscription")
+        .new_span(
+            &"Subscriptions.errSub"
+                .with_field("err", "Definitely not an error, trust me")
+                .with_strict_fields(true),
+        )
+        .close_exited("resolve_validated_subscription")
+        .close_exited("resolve_into_stream")
+        .enter("Subscriptions.errSub")
+        .try_close("Subscriptions.errSub")
+        .simple_span("next")
+        .close_exited("Subscriptions.errSub");
+}
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index 9e10eae2d..06eada4f5 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -277,13 +277,6 @@ pub(crate) enum OnMethod {
     /// [`Executor`]: juniper::Executor
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
     Executor,
-
-    /// [`Span`] passed into a [GraphQL field][2] resolving method.
-    ///
-    /// [`Span`]: tracing::Span
-    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    #[cfg(feature = "tracing")]
-    Span,
 }
 
 impl OnMethod {
@@ -369,9 +362,6 @@ impl OnMethod {
             },
 
             Self::Executor => quote! { &executor },
-
-            #[cfg(feature = "tracing")]
-            Self::Span => quote! { _tracing_span.clone() },
         }
     }
 
@@ -396,8 +386,6 @@ impl OnMethod {
                 }
             }
             Self::Context(_) | Self::Executor => quote!(),
-            #[cfg(feature = "tracing")]
-            Self::Span => quote!(),
         }
     }
 
@@ -435,8 +423,6 @@ impl OnMethod {
                     Some(Self::Context(argument.ty.unreferenced().clone()))
                 }
                 "executor" | "_executor" => Some(Self::Executor),
-                #[cfg(feature = "tracing")]
-                "tracing_span" | "_span" | "_tracing_span" => Some(Self::Span),
                 _ => None,
             };
             if arg.is_some() {
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 282d5fc34..47bf29743 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -447,8 +447,8 @@ impl Definition {
 
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
-        let (res, getters) = if self.is_method() {
-            let (args, getters): (Vec<_>, Vec<_>) = self
+        let (res, args) = if self.is_method() {
+            let (args, arg_getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
@@ -470,7 +470,7 @@ impl Definition {
             } else {
                 quote! { Self::#ident(#rcv #( #args ),*) }
             };
-            (res, quote!(#( #getters )*))
+            (res, quote!(#( #arg_getters )*))
         } else {
             ty = parse_quote! { _ };
             (quote! { &self.#ident }, quote!())
@@ -479,14 +479,16 @@ impl Definition {
         let resolving_code = gen::sync_resolving_code();
         let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
         let trace_sync = if_tracing_enabled!(tracing::sync_tokens(traced_ty, self));
+        let record_err = if_tracing_enabled!(tracing::record_err_sync(traced_ty, self));
 
         Some(quote! {
             #name => {
-                #getters
+                #args
                 #span
                 #trace_sync
 
                 let res: #ty = #res;
+                #record_err
                 #resolving_code
             }
         })
@@ -507,8 +509,8 @@ impl Definition {
     ) -> TokenStream {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
-        let (mut fut, fields) = if self.is_method() {
-            let (args, getters): (Vec<_>, Vec<_>) = self
+        let (mut fut, args) = if self.is_method() {
+            let (args, arg_getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
@@ -530,7 +532,7 @@ impl Definition {
             } else {
                 quote! { Self::#ident(#rcv #( #args ),*) }
             };
-            (fut, quote!( #( #getters )*))
+            (fut, quote!( #( #arg_getters )*))
         } else {
             ty = parse_quote! { _ };
             (quote! { &self.#ident }, quote!())
@@ -547,15 +549,17 @@ impl Definition {
 
         let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
         let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
+        let record_err = if_tracing_enabled!(tracing::record_err_async(traced_ty, self));
 
         let resolving_code = gen::async_resolving_code(Some(&ty), trace_async);
 
         quote! {
             #name => {
-                #fields
+                #args
                 #span
 
                 let fut = #fut;
+                #record_err
                 #resolving_code
             }
         }
@@ -577,7 +581,7 @@ impl Definition {
         let (name, mut ty, ident) = (&self.name, self.ty.clone(), &self.ident);
 
         let (mut fut, args) = if self.is_method() {
-            let (args, getters): (Vec<_>, Vec<_>) = self
+            let (args, arg_getters): (Vec<_>, Vec<_>) = self
                 .arguments
                 .as_ref()
                 .unwrap()
@@ -596,7 +600,7 @@ impl Definition {
 
             (
                 quote! { Self::#ident(#rcv #( #args ),*) },
-                quote! { #( #getters )* },
+                quote! { #( #arg_getters )* },
             )
         } else {
             ty = parse_quote! { _ };
@@ -608,6 +612,8 @@ impl Definition {
 
         let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
         let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
+        let trace_stream = if_tracing_enabled!(tracing::stream_tokens(traced_ty, self));
+        let record_err = if_tracing_enabled!(tracing::record_err_stream(traced_ty, self));
 
         quote! {
             #name => {
@@ -616,13 +622,14 @@ impl Definition {
 
                 ::juniper::futures::FutureExt::boxed(async move {
                     let res: #ty = #fut.await;
+                    #record_err
                     let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
                     let executor = executor.as_owned_executor();
                     let f = ::juniper::futures::StreamExt::then(res, move |res| {
                         let executor = executor.clone();
                         let res2: ::juniper::FieldResult<_, #scalar> =
                             ::juniper::IntoResolvable::into(res, executor.context());
-                        async move {
+                        let fut = async move {
                             let ex = executor.as_executor();
                             match res2 {
                                 Ok(Some((ctx, r))) => {
@@ -632,9 +639,13 @@ impl Definition {
                                         .map_err(|e| ex.new_error(e))
                                 }
                                 Ok(None) => Ok(::juniper::Value::null()),
-                                Err(e) => Err(ex.new_error(e)),
+                                Err(e) => {
+                                    Err(ex.new_error(e))
+                                },
                             }
-                        }
+                        };
+                        #trace_stream
+                        fut
                     });
                     #trace_async
                     Ok(::juniper::Value::Scalar::<
diff --git a/juniper_codegen/src/graphql_object/derive.rs b/juniper_codegen/src/graphql_object/derive.rs
index 350e50d25..0660e30b5 100644
--- a/juniper_codegen/src/graphql_object/derive.rs
+++ b/juniper_codegen/src/graphql_object/derive.rs
@@ -34,7 +34,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
 
 /// Expands into generated code a `#[derive(GraphQLObject)]` macro placed on a
 /// Rust struct.
-fn expand_struct(mut ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
+fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
     let attr = Attr::from_attrs("graphql", &ast.attrs)?;
 
     let struct_span = ast.span();
@@ -110,7 +110,7 @@ fn expand_struct(mut ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
         _operation: PhantomData,
 
         #[cfg(feature = "tracing")]
-        tracing: tracing::Rule::from_attrs_and_strip("tracing", &mut ast.attrs)?,
+        tracing: tracing::Rule::from_attrs("tracing", &ast.attrs)?,
     })
 }
 
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index 1e75fc0cb..7920f1910 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -28,6 +28,10 @@ pub struct Attr {
     /// Skipped arguments on `fn` resolvers.
     skip: HashMap<String, syn::Ident>,
 
+    // Only relevant when returned type is `Result<Ok, Err>`.
+    /// Whether error returned by resolver should be recorded in [`Span`].
+    record_err: bool,
+
     /// Custom fields.
     fields: Vec<Field>,
 }
@@ -111,6 +115,9 @@ impl Parse for Attr {
                     input.parse::<Token![=]>()?;
                     attr.target = Some(input.parse()?);
                 }
+                "err" => {
+                    attr.record_err = true;
+                }
                 "skip" => {
                     let skipped_fields;
                     syn::parenthesized!(skipped_fields in input);
@@ -452,30 +459,40 @@ pub fn span_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     let span_name = format!("{}.{}", ty.name(), name);
     let span_name = syn::LitStr::new(&span_name, name.span());
 
-    let args = field.args().into_iter().filter_map(|arg| {
-        let name = arg.name();
-        let raw_name = arg.raw_name();
-        let arg_name = syn::LitStr::new(name, raw_name.span());
+    let mut args: Vec<_> = field
+        .args()
+        .into_iter()
+        .filter_map(|arg| {
+            let name = arg.name();
+            let raw_name = arg.raw_name();
+            let arg_name = syn::LitStr::new(name, raw_name.span());
+
+            field
+                .instrument()
+                .map(|t| t.skip.get(&raw_name.to_string()))
+                .flatten()
+                .is_none()
+                .then(|| {
+                    quote!(
+                        #arg_name = ::juniper::tracing::field::debug(&#raw_name)
+                    )
+                })
+        })
+        .collect();
 
-        field
-            .instrument()
-            .map(|t| t.skip.get(&raw_name.to_string()))
-            .flatten()
-            .is_none()
-            .then(|| {
-                quote!(
-                    #arg_name = ::juniper::tracing::field::debug(&#raw_name)
-                )
-            })
-    });
+    if field
+        .instrument()
+        .map(|attr| attr.record_err)
+        .unwrap_or(false)
+    {
+        args.push(quote!(err = ::juniper::tracing::field::Empty));
+    }
 
-    let args: Vec<_> = if let Some(tracing) = field.instrument() {
+    if let Some(tracing) = field.instrument() {
         let additional_fields = tracing.fields.iter().map(|f| quote!(#f));
 
-        args.chain(additional_fields).collect()
-    } else {
-        args.collect()
-    };
+        args.extend(additional_fields);
+    }
 
     let level = field
         .instrument()
@@ -521,6 +538,79 @@ pub fn async_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStre
     )
 }
 
+pub fn record_err_async(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field)
+        || !field
+            .instrument()
+            .map(|attr| attr.record_err)
+            .unwrap_or(false)
+    {
+        return quote!();
+    }
+    quote! (
+        let fut = <_ as ::juniper::futures::TryFutureExt>::map_err(fut, |e| {
+            ::juniper::tracing::Span::current()
+                .record("err", &::juniper::tracing::field::display(&e));
+            e
+        });
+    )
+}
+
+/// Returns code to start tracing of a single iteration within `Stream`, unlike
+/// simple resolvers subscriptions have two layers of `Span`s, one to the whole
+/// `Stream` that represents this subscription and the second one is for individual
+/// resolvers.
+pub fn stream_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field) {
+        return quote!();
+    }
+    // Sub span should have same level.
+    let level = field
+        .instrument()
+        .map(|t| t.level.as_ref())
+        .flatten()
+        .map(|l| match l.value().as_str() {
+            "trace" => quote!(TRACE),
+            "debug" => quote!(DEBUG),
+            "info" => quote!(INFO),
+            "warn" => quote!(WARN),
+            "error" => quote!(ERROR),
+            l => abort!(syn::Error::new(
+                l.span(),
+                format!(
+                    "Unsupported tracing level: {}, \
+                     supported values: trace, debug, info, warn, error",
+                    l,
+                ),
+            )),
+        })
+        .unwrap_or_else(|| quote!(INFO));
+    quote!(
+        let fut = <_ as ::juniper::tracing_futures::Instrument>::instrument(
+            fut,
+            ::juniper::tracing::span!(::juniper::tracing::Level::#level, "next"),
+        );
+    )
+}
+
+pub fn record_err_stream(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field)
+        || !field
+            .instrument()
+            .map(|attr| attr.record_err)
+            .unwrap_or(false)
+    {
+        return quote!();
+    }
+    quote! (
+        let res = <_ as ::juniper::futures::TryStreamExt>::map_err(res, |e| {
+            ::juniper::tracing::Span::current()
+                .record("err", &::juniper::tracing::field::display(&e));
+            e
+        });
+    )
+}
+
 /// Returns code to start tracing of sync block
 pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
@@ -529,6 +619,20 @@ pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     quote!(let _tracing_guard = _tracing_span.enter();)
 }
 
+pub fn record_err_sync(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field)
+        || !field
+            .instrument()
+            .map(|attr| attr.record_err)
+            .unwrap_or(false)
+    {
+        return quote!();
+    }
+    quote!(if let Err(e) = &res {
+        ::juniper::tracing::Span::current().record("err", &::juniper::tracing::field::display(&e));
+    })
+}
+
 mod impls {
     use crate::{common::field, graphql_interface as interface, graphql_object as object};
 

From c22ef43f09c4149d61479df58879efe7f827ec75 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Tue, 17 Aug 2021 12:04:05 +0300
Subject: [PATCH 71/72] Remove redundant code

---
 docs/book/content/tracing/index.md           | 44 +++++++---
 examples/tracing_support/src/main.rs         | 23 +++--
 juniper/src/tests/fixtures/tracing/mod.rs    |  9 +-
 juniper/src/tests/fixtures/tracing/schema.rs | 80 ++++++++---------
 juniper/src/tests/tracing_tests.rs           | 92 ++++++++------------
 juniper_codegen/src/common/field/arg.rs      |  5 +-
 juniper_codegen/src/common/field/mod.rs      | 29 ++++--
 juniper_codegen/src/graphql_interface/mod.rs | 13 +--
 juniper_codegen/src/graphql_object/mod.rs    |  4 +-
 juniper_codegen/src/lib.rs                   | 37 +++-----
 juniper_codegen/src/tracing.rs               | 24 ++++-
 juniper_codegen/src/util/mod.rs              |  5 +-
 12 files changed, 185 insertions(+), 180 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index 7e82046e1..e59b816f9 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -1,6 +1,6 @@
 # Tracing
 
-Juniper has optional support for the [tracing] crate for instrumentation.
+Starting from version `0.15.8` Juniper has optional support for the [tracing] crate for instrumentation.
 
 This feature is off by default and can be enabled via the `tracing` feature.
 
@@ -103,10 +103,25 @@ async fn main() {
 
 Juniper has it's own `#[instrument]` attribute, that can only be used with
 GraphQL Objects and Interfaces. In most aspects it mimics behavior of the original
-`#[instrument]` attribute from [tracing] crate including fields, sigils, and you
-could use it as a reference. First and the only key deference you should keep in
-mind, `#[instrument]` applied implicitly to **all** resolvers if the `tracing`
-feature is enabled.
+`#[instrument]` attribute from [tracing] crate including fields, sigils, so you
+could use it as a reference. First key deference you should keep in mind, `#[instrument]`
+applied implicitly to **all** resolvers if the `tracing` feature is enabled.
+Second and most significant difference is generated [`Span`]s. To fully understand
+this you should know how GraphQL Objects/Interfaces are actually resolved under
+the hood, long story short there is a lot of generated code and two methods
+`resolve_field` and `resolve_field_async` that map your resolvers to fields,
+and then recursively resolve fields of returned value (if it's not a `Scalar`).
+`#[instrument]` from [tracing] knows nothing about Juniper and  this, so it will
+only wrap method in [`Span`], ignoring recursive part capturing tip of the
+iceberg, so this will lead to plain sequence of resolver [`Span`]s, where you
+could hardly understand order and relations between each  resolver. On the other
+hand `#[instrument]` from Juniper is part of top-level macro so it's aware of
+all tricks performed by Juniper and will be expanded as part of `resolve_field`
+or `resolve_field_async`, [`Span`]s generated this way will capture full lifespan
+of value, including how it's fields resolved which results in more tree-like
+[`Span`] structure where you could easily navigate through, using tools like
+[Jaeger]. As a bonus you'll get [`Span`] names, which refer to your schema instead
+of code.
 
 ## Skipping field resolvers
 
@@ -152,7 +167,7 @@ on top-level to trace specific group of fields or not to trace at all.
  - Use `sync` to trace only synchronous resolvers (struct fields and `fn`s).
  - Use `async` to trace only asynchronous resolvers (`async fn`s) and
 subscriptions.
- - Use `only` to trace only fields marked with `#[graphql(tracing(only))]`
+ - Use `only` to trace only fields marked with `#[graphql(tracing(only))]`.
  - Use `skip_all` to skip tracing of all fields.
 
 ### Example
@@ -358,7 +373,7 @@ on user code so should be used with care.
 ```rust
 # extern crate juniper;
 # use std::fmt;
-# use juniper::graphql_object;
+# use juniper::{graphql_object, FieldError};
 #
 # fn main() {}
 #
@@ -376,6 +391,12 @@ impl fmt::Display for MyError {
     }
 }
 
+impl<S> juniper::IntoFieldError<S> for MyError {
+    fn into_field_error(self) -> FieldError<S> {
+        FieldError::new(self, juniper::Value::Null)
+    }
+}
+
 # #[graphql_object]
 # impl Query {
 // Under the hood it will create `Empty` field with name "err", and will record
@@ -392,9 +413,11 @@ async fn conditional_error() -> Result<i32, MyError> {
 #   let condition = false;
     // At this point we manually check whether result is error or not and if
     // condition is met and only then record error.
-    if let Err(err) = &res && condition {
-        tracing::Span::current().record("err", &field::display(err));
-    }
+    if condition {
+        if let Err(err) = &res  {
+            tracing::Span::current().record("err", &field::display(err));
+        }
+    } 
     res
 }
 # }
@@ -424,3 +447,4 @@ should be handled manually.
 [`skip`]: https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html#skipping-fields
 [`Span`]: https://docs.rs/tracing/0.1.26/tracing/struct.Span.html
 [`field::Empty`]: https://docs.rs/tracing/0.1.26/tracing/field/struct.Empty.html
+[Jaeger]: https://www.jaegertracing.io
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index 14c9803a7..d923a0f4f 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -30,7 +30,7 @@ struct User {
 
 #[graphql_object(Context = Context)]
 impl User {
-    // `id` can be resolved pretty straightforward so we mark it with `no_trace`
+    // `id` can be resolved pretty straight-forward so we mark it with `ignore`
     #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
@@ -56,12 +56,11 @@ struct SyncTracedUser {
     name: String,
 }
 
-// Only sync `fn`s will be traced if they're not marked with `#[tracing(no_trace)]`
-// it works similarly with `#[graphql_interface]`
+// Only sync `fn`s will be traced if they're not marked with `#[graphql(tracing(ignore))]`.
 #[graphql_object(Context = Context)]
 #[tracing(sync)]
 impl SyncTracedUser {
-    // Won't be traced because it's marked with `no_trace`
+    // Won't be traced because it's marked with `ignore`
     #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
@@ -81,13 +80,12 @@ impl SyncTracedUser {
 #[derive(Clone, Debug, GraphQLObject)]
 #[tracing(only)]
 struct ComplexDerivedUser {
-    // This shouldn't be traced because it's not marked with `#[tracing(only)]`
+    // This shouldn't be traced because it's not marked with `tracing(only)`
     id: i32,
-    // This is the only field that will be traced because it's marked with `#[tracing(only)]`
+    // This is the only field that will be traced because it's marked with `tracing(only)`
     #[graphql(tracing(only))]
     kind: UserKind,
-    // This shouldn't be traced because of `ignore`.
-    #[graphql(tracing(ignore))]
+    // This also shouldn't be traced because there is no `tracing(only)`.
     name: String,
 }
 
@@ -138,7 +136,6 @@ impl Query {
         }
     }
 
-    /// Double the provided number.
     async fn double(x: i32) -> Result<i32, FieldError> {
         Ok(x * 2)
     }
@@ -193,7 +190,7 @@ async fn main() {
     // If you use tracing with something like `jaeger_opentracing` span with name
     // 'Query.guest' which has field 'name' with value '"Not Bob"' and 1 child span
     // 'User.kind'. There won't be traces to 'User.id' because we marked it with
-    // `#[tracing(no_trace)]`
+    // `#[graphql(tracing(ignore))]`
     let query = "{ guest(id: 1, name: \"Not Bob\") { id  kind} }";
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
         .await
@@ -201,15 +198,15 @@ async fn main() {
 
     // Here you'll see span 'Query.syncUser' with one child span
     // 'SyncTracedUser.kind' because it's synchronous and not marked with
-    // `#[tracing(no_trace)]`.
+    // `#[graphql(tracing(ignore))]`.
     let query = "{ syncUser { id name kind} }";
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
         .await
         .unwrap();
 
     // Here you'll see span 'Query.complexUser' with one child span
-    // 'ComplexDerivedUser.kind' because it's marked with `#[tracing(complex)]`
-    // and not marked with `#[tracing(no_trace)]`.
+    // 'ComplexDerivedUser.kind' because it's marked with
+    // `#[graphql(tracing(only))]`.
     let query = "{ complexUser { id name kind }}";
     let (_, _errors) = juniper::execute(query, None, &root, &vars, &ctx)
         .await
diff --git a/juniper/src/tests/fixtures/tracing/mod.rs b/juniper/src/tests/fixtures/tracing/mod.rs
index 6d0cfffc6..5c733b7a5 100644
--- a/juniper/src/tests/fixtures/tracing/mod.rs
+++ b/juniper/src/tests/fixtures/tracing/mod.rs
@@ -6,8 +6,6 @@ pub mod schema;
 
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
-// use tracing_core::Subscriber;
-
 use crate::tracing::{
     field::{Field, Visit},
     span::{self, Attributes, Record},
@@ -209,7 +207,7 @@ impl Subscriber for TestSubscriber {
     }
 }
 
-/// Wrapper representing span tree received from [`TestSubscriber`].
+/// Wrapper that represents tree of [`Span`]s collected by [`TestSubscriber`].
 #[derive(Debug)]
 pub struct SubscriberAssert {
     name_to_span: HashMap<span::Id, String>,
@@ -405,7 +403,7 @@ impl SubscriberAssert {
             .try_close(span)
     }
 
-    /// Checks whether next to steps is creation of a new span with the given
+    /// Checks whether next two steps is creation of a new span with the given
     /// name and entering it.
     pub fn enter_new_span<S: AsSpan + ?Sized>(self, span: &S) -> Self {
         self.new_span(span).enter(span).re_enter(span)
@@ -419,9 +417,6 @@ impl SubscriberAssert {
 
     /// Checks whether next two steps is exiting and re-entering the same span
     /// with the given name.
-    ///
-    /// This may be useful in case of tracing when sync object is resolved in
-    /// async context.
     pub fn re_enter<S: AsSpan + ?Sized>(self, span: &S) -> Self {
         use SubscriberEvent as Ev;
 
diff --git a/juniper/src/tests/fixtures/tracing/schema.rs b/juniper/src/tests/fixtures/tracing/schema.rs
index c0889dddc..79d401bcd 100644
--- a/juniper/src/tests/fixtures/tracing/schema.rs
+++ b/juniper/src/tests/fixtures/tracing/schema.rs
@@ -37,19 +37,11 @@ impl Database {
     pub fn non_traced_query(&self, id: i32) -> Option<String> {
         self.inner.get(&id).cloned()
     }
-}
-
-pub struct Resolver;
-
-impl Resolver {
-    #[instrument]
-    pub fn resolve_sync() -> i32 {
-        1
-    }
 
-    #[instrument]
-    pub async fn resolve_async() -> i32 {
-        1
+    /// Sync query mock, instrumented by [`tracing`] crate.
+    #[instrument(skip(self))]
+    pub async fn async_traced_query(&self, id: i32) -> Option<String> {
+        self.inner.get(&id).cloned()
     }
 }
 
@@ -132,21 +124,21 @@ impl Query {
         SkipAllDerived::default()
     }
 
-    /// Returns GraphQL object marked with `tracing(complex)` in sync manner.
-    fn complex_sync() -> Complex {
-        Complex
+    /// Returns GraphQL object marked with `tracing(only)` in sync manner.
+    fn only_sync() -> Only {
+        Only
     }
 
-    /// Returns GraphQL object marked with `tracing(complex)` in async manner.
-    async fn complex_async() -> Complex {
-        Complex
+    /// Returns GraphQL object marked with `tracing(only)` in async manner.
+    async fn only_async() -> Only {
+        Only
     }
 
-    /// Returns derived GraphQL object marked with `tracing(complex)`.
-    fn complex_derived() -> DerivedComplex {
-        DerivedComplex {
-            complex: false,
-            another_complex: false,
+    /// Returns derived GraphQL object marked with `tracing(only)`.
+    fn only_derived() -> DerivedOnly {
+        DerivedOnly {
+            only: false,
+            another_only: false,
             sync: 0,
         }
     }
@@ -171,19 +163,19 @@ impl Query {
         InterfacedSkipAllValue::SkipAll(SkipAll)
     }
 
-    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(complex)`.
-    fn erased_complex() -> InterfacedComplexValue {
-        InterfacedComplexValue::Complex(Complex)
+    /// Returns GraphQL object wrapped in GraphQL interface marked with `tracing(only)`.
+    fn erased_only() -> InterfacedOnlyValue {
+        InterfacedOnlyValue::Only(Only)
     }
 
     /// Sync fn that uses instrumented function under the hood.
-    fn sub_resolver() -> i32 {
-        Resolver::resolve_sync()
+    fn sub_resolver(context: &Database) -> Option<String> {
+        context.traced_query(42)
     }
 
     /// Async fn that uses instrumented function under the hood.
-    async fn sub_async_resolver() -> i32 {
-        Resolver::resolve_async().await
+    async fn sub_async_resolver(context: &Database) -> Option<String> {
+        context.async_traced_query(42).await
     }
 
     /// Fn that has custom field marked with debug sigil (`?`).
@@ -246,6 +238,7 @@ pub struct Subscriptions;
 
 #[graphql_subscription(context = Database)]
 impl Subscriptions {
+    /// Subscription that emits `Bar`s.
     async fn bar_sub(id: i32) -> BoxStream<'static, Bar> {
         let items = [Bar { id: id + 1 }, Bar { id: id + 2 }];
 
@@ -549,11 +542,11 @@ pub struct SkipAllDerived {
 }
 
 /// GraphQL object marked with `tracing(only)`.
-pub struct Complex;
+pub struct Only;
 
-#[graphql_object(impl = [InterfacedComplexValue])]
+#[graphql_object(impl = [InterfacedOnlyValue])]
 #[tracing(only)]
-impl Complex {
+impl Only {
     #[graphql(tracing(only))]
     pub fn sync_fn(&self) -> i32 {
         1
@@ -569,17 +562,17 @@ impl Complex {
     }
 }
 
-build_impl!(Complex, InterfacedComplex);
+build_impl!(Only, InterfacedOnly);
 
 /// Derived GraphQL object marked with `tracing(only)`.
 #[derive(GraphQLObject)]
 #[tracing(only)]
-pub struct DerivedComplex {
+pub struct DerivedOnly {
     #[graphql(tracing(only))]
-    complex: bool,
+    only: bool,
     #[graphql(tracing(only))]
     #[instrument(fields(test = "magic"))]
-    another_complex: bool,
+    another_only: bool,
 
     /// Simple field
     sync: i32,
@@ -615,24 +608,25 @@ trait InterfacedSkipAll {
     async fn async_fn(&self) -> i32;
 }
 
-#[graphql_interface(for = [Complex], async)]
+#[graphql_interface(for = [Only], async)]
 #[tracing(only)]
-trait InterfacedComplex {
+trait InterfacedOnly {
     fn sync_fn(&self) -> i32;
     #[graphql(tracing(only))]
     async fn async_fn(&self) -> i32;
 }
 
+/// Value that used to test debug and display sigils.
 struct Sigil;
 
-impl std::fmt::Debug for Sigil {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Debug for Sigil {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt.write_str("Debug Sigil")
     }
 }
 
-impl std::fmt::Display for Sigil {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl fmt::Display for Sigil {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt.write_str("Display Sigil")
     }
 }
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index 458b6c0ab..e588b4ded 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -13,10 +13,12 @@ use crate::{
 
 type TestSchema<'a> = RootNode<'a, Query, EmptyMutation<Database>, Subscriptions>;
 
+/// Shorthand to [`TestSchema`] initialization.
 fn init_schema<'a>() -> TestSchema<'a> {
     TestSchema::new(Query, EmptyMutation::<Database>::new(), Subscriptions)
 }
 
+/// Initializes new tracer, sets it as default and returns guard.
 fn init_tracer() -> (TestSubscriber, DefaultGuard) {
     let subscriber = TestSubscriber::new();
 
@@ -121,7 +123,7 @@ async fn records_sub_spans() {
 }
 
 #[tokio::test]
-async fn test_no_trace_field() {
+async fn test_ignored_field() {
     let doc = r#"
         {
             foo {
@@ -368,14 +370,14 @@ async fn graphql_object_trace_arg() {
             skipAllDerived {
                 sync
             }
-            complexSync {
+            onlySync {
                 syncFn
                 asyncFn
                 simpleField
             }
-            complexDerived {
-                complex
-                anotherComplex
+            onlyDerived {
+                only
+                anotherOnly
                 sync
             }
         }
@@ -409,20 +411,20 @@ async fn graphql_object_trace_arg() {
         // There shouldn't be any spans because `SkipAll` and `SkipAllDerived` marked with "skip-all"
         .simple_span("Query.skipAll")
         .simple_span("Query.skipAllDerived")
-        .enter_new_span("Query.complexSync")
-        .simple_span("Complex.syncFn")
-        .simple_span("Complex.asyncFn")
+        .enter_new_span("Query.onlySync")
+        .simple_span("Only.syncFn")
+        .simple_span("Only.asyncFn")
         // There shouldn't be any span for `simpleField`
-        .close_exited("Query.complexSync")
-        .enter_new_span("Query.complexDerived")
-        .simple_span("DerivedComplex.complex")
+        .close_exited("Query.onlySync")
+        .enter_new_span("Query.onlyDerived")
+        .simple_span("DerivedOnly.only")
         .simple_span(
-            &"DerivedComplex.anotherComplex"
+            &"DerivedOnly.anotherOnly"
                 .with_field("test", "\"magic\"")
                 .with_strict_fields(true),
         )
-        // There shouldn't be span for `sync` because it's not marked with `#[tracing(complex)]`
-        .close_exited("Query.complexDerived")
+        // There shouldn't be span for `sync` because it's not marked with `#[tracing(only)]`
+        .close_exited("Query.onlyDerived")
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
@@ -447,7 +449,7 @@ async fn graphql_interface_trace_arg() {
                 syncFn
                 asyncFn
             }
-            erasedComplex {
+            erasedOnly {
                 syncFn
                 asyncFn
             }
@@ -477,9 +479,9 @@ async fn graphql_interface_trace_arg() {
         .simple_span("InterfacedAsync.asyncFn")
         .close_exited("Query.erasedAsync")
         .simple_span("Query.erasedSkipAll")
-        .enter_new_span("Query.erasedComplex")
-        .simple_span("InterfacedComplex.asyncFn")
-        .close_exited("Query.erasedComplex")
+        .enter_new_span("Query.erasedOnly")
+        .simple_span("InterfacedOnly.asyncFn")
+        .close_exited("Query.erasedOnly")
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
 }
@@ -499,13 +501,9 @@ async fn subscription_tracing() {
     let (handle, _guard) = init_tracer();
 
     let request = crate::http::GraphQLRequest::new(doc.to_owned(), None, None);
-
     let response = crate::http::resolve_into_stream(&request, &schema, &database).await;
-
     assert!(response.is_ok());
-
     let (values, errors) = response.unwrap();
-
     assert_eq!(errors.len(), 0, "Should return no errors");
 
     // cannot compare with `assert_eq` because
@@ -516,9 +514,7 @@ async fn subscription_tracing() {
     };
 
     assert!(response_value_object.is_some());
-
     let response_returned_object = response_value_object.unwrap();
-
     let fields = response_returned_object.into_iter();
 
     let mut names = vec![];
@@ -597,22 +593,18 @@ async fn sub_resolvers() {
         .enter_new_span("execute_validated_query_async")
         .enter_new_span("Query.subResolver")
         // Sync sub resolver in async context
-        .simple_span("resolve_sync")
+        .simple_span("traced_query")
         .close_exited("Query.subResolver")
         .enter_new_span("Query.subAsyncResolver")
         // Async sub resolver in async context
-        .simple_span("resolve_async")
+        .simple_span("async_traced_query")
         .close_exited("Query.subAsyncResolver")
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
 
     handle.clear();
 
-    let doc_sync = r#"
-        {
-            subResolver
-        }
-        "#;
+    let doc_sync = r#"{ subResolver }"#;
 
     let res = juniper::execute_sync(doc_sync, None, &schema, &Variables::new(), &database);
     assert!(res.is_ok(), "Should be ok");
@@ -625,7 +617,7 @@ async fn sub_resolvers() {
         .enter_new_span("execute_validated_query")
         .enter_new_span("Query.subResolver")
         // Sync sub resolver in sync context
-        .simple_span("resolve_sync")
+        .simple_span("traced_query")
         .close_exited("Query.subResolver")
         .close_exited("execute_validated_query")
         .close_exited("execute_sync");
@@ -660,11 +652,7 @@ async fn tracing_compat_sigil() {
 
 #[tokio::test]
 async fn tracing_compat_empty_field() {
-    let doc = r#"
-    {
-        emptyField
-    }
-    "#;
+    let doc = r#"{ emptyField }"#;
     let schema = init_schema();
     let database = Database::new();
     let (handle, _guard) = init_tracer();
@@ -683,9 +671,9 @@ async fn tracing_compat_empty_field() {
                 .with_field("magic", "\"really magic\"")
                 .with_strict_fields(true),
         )
-        // This happens because we're passing owned copy of `Span` in
-        // `empty_field`, and `Span` attempts to close itself because
-        // of `Drop` implementation.
+        // This happens because we're creating owned copy of `Span` with
+        // `tracing::Span::current()` inside `empty_field` resolver, and
+        // once `Span` attempts to close itself because of `Drop`.
         .try_close("Query.emptyField")
         // At this point we resolved `Query.emptyField` and all of it's child
         // spans (which don't exists, in this test case) and it's now safe to
@@ -719,14 +707,14 @@ async fn tracing_compat_err_recording() {
         .simple_span(
             &"Query.recordErrSync"
                 .with_field("shouldErr", "false")
-                // Should be exactly 1 field because err was not recorded.
+                // Should be exactly one field because err was not recorded.
                 .with_strict_fields(true),
         )
         .enter_new_span(
             &"Query.recordErrSync"
                 .with_field("shouldErr", "true")
                 .with_field("err", "Definitely not an error, trust me")
-                // Here should be present both fields because error was returned.
+                // Here should be present exactly two fields because error was returned.
                 .with_strict_fields(true),
         )
         .try_close("Query.recordErrSync")
@@ -748,14 +736,14 @@ async fn tracing_compat_err_recording() {
         .simple_span(
             &"Query.recordErrSync"
                 .with_field("shouldErr", "false")
-                // Should be exactly 1 field because err was not recorded.
+                // Should be exactly one field because err was not recorded.
                 .with_strict_fields(true),
         )
         .enter_new_span(
             &"Query.recordErrSync"
                 .with_field("shouldErr", "true")
                 .with_field("err", "Definitely not an error, trust me")
-                // Here should be present both fields because error was returned.
+                // Here should be present exactly two fields because error was returned.
                 .with_strict_fields(true),
         )
         .try_close("Query.recordErrSync")
@@ -787,14 +775,14 @@ async fn tracing_compat_err_recording() {
         .simple_span(
             &"Query.recordErrAsync"
                 .with_field("shouldErr", "false")
-                // Should be exactly 1 field because err was not recorded.
+                // Should be exactly one field because err was not recorded.
                 .with_strict_fields(true),
         )
         .enter_new_span(
             &"Query.recordErrAsync"
                 .with_field("shouldErr", "true")
                 .with_field("err", "Definitely not an error, trust me")
-                // Here should be present both fields because error was returned.
+                // Here should be exactly two fields because error was returned.
                 .with_strict_fields(true),
         )
         .try_close("Query.recordErrAsync")
@@ -805,23 +793,15 @@ async fn tracing_compat_err_recording() {
 
 #[tokio::test]
 async fn tracing_compat_err_on_subscriptions() {
-    let doc = r#"
-        subscription {
-            errSub
-        }
-        "#;
+    let doc = r#"subscription { errSub }"#;
     let schema = init_schema();
     let database = Database::new();
     let (handle, _guard) = init_tracer();
 
     let request = crate::http::GraphQLRequest::new(doc.to_owned(), None, None);
-
     let response = crate::http::resolve_into_stream(&request, &schema, &database).await;
-
     assert!(response.is_ok());
-
     let (values, errors) = response.unwrap();
-
     assert_eq!(errors.len(), 0, "Should return no errors");
 
     // cannot compare with `assert_eq` because
@@ -832,9 +812,7 @@ async fn tracing_compat_err_on_subscriptions() {
     };
 
     assert!(response_value_object.is_some());
-
     let response_returned_object = response_value_object.unwrap();
-
     let fields = response_returned_object.into_iter();
 
     let mut names = vec![];
diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs
index 06eada4f5..765f4e24d 100644
--- a/juniper_codegen/src/common/field/arg.rs
+++ b/juniper_codegen/src/common/field/arg.rs
@@ -232,11 +232,14 @@ pub(crate) struct OnField {
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments
     pub(crate) ty: syn::Type,
 
-    /// Name of this [GraphQL field argument][2] in GraphQL schema.
+    /// Name of this [GraphQL field argument][1] in GraphQL schema.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments
     pub(crate) name: String,
 
+    /// Raw name identifier of this [Graphql field argument][1] in GraphQL schema.
+    ///
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments
     pub(crate) raw_name: syn::Ident,
 
     /// [Description][2] of this [GraphQL field argument][1] to put into GraphQL
diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs
index 47bf29743..b653c795e 100644
--- a/juniper_codegen/src/common/field/mod.rs
+++ b/juniper_codegen/src/common/field/mod.rs
@@ -29,6 +29,21 @@ use crate::{
 #[cfg(feature = "tracing")]
 use crate::tracing;
 
+/// Expands to `code` if `tracing` feature of this crate is enabled, otherwise returns an
+/// empty [`TokenStream`].
+macro_rules! if_tracing_enabled {
+    ($code: expr) => {{
+        #[cfg(feature = "tracing")]
+        {
+            $code
+        }
+        #[cfg(not(feature = "tracing"))]
+        {
+            ::quote::quote!()
+        }
+    }};
+}
+
 pub(crate) use self::arg::OnMethod as MethodArgument;
 
 /// Available metadata (arguments) behind `#[graphql]` attribute placed on a
@@ -611,8 +626,8 @@ impl Definition {
         }
 
         let span = if_tracing_enabled!(tracing::span_tokens(traced_ty, self));
-        let trace_async = if_tracing_enabled!(tracing::async_tokens(traced_ty, self));
         let trace_stream = if_tracing_enabled!(tracing::stream_tokens(traced_ty, self));
+        let trace_resolver = if_tracing_enabled!(tracing::stream_next_tokens(traced_ty, self));
         let record_err = if_tracing_enabled!(tracing::record_err_stream(traced_ty, self));
 
         quote! {
@@ -625,7 +640,7 @@ impl Definition {
                     #record_err
                     let res = ::juniper::IntoFieldResult::<_, #scalar>::into_result(res)?;
                     let executor = executor.as_owned_executor();
-                    let f = ::juniper::futures::StreamExt::then(res, move |res| {
+                    let stream = ::juniper::futures::StreamExt::then(res, move |res| {
                         let executor = executor.clone();
                         let res2: ::juniper::FieldResult<_, #scalar> =
                             ::juniper::IntoResolvable::into(res, executor.context());
@@ -639,18 +654,16 @@ impl Definition {
                                         .map_err(|e| ex.new_error(e))
                                 }
                                 Ok(None) => Ok(::juniper::Value::null()),
-                                Err(e) => {
-                                    Err(ex.new_error(e))
-                                },
+                                Err(e) => Err(ex.new_error(e)),
                             }
                         };
-                        #trace_stream
+                        #trace_resolver
                         fut
                     });
-                    #trace_async
+                    #trace_stream
                     Ok(::juniper::Value::Scalar::<
                         ::juniper::ValuesStream::<#scalar>
-                    >(::juniper::futures::StreamExt::boxed(f)))
+                    >(::juniper::futures::StreamExt::boxed(stream)))
                 })
             }
         }
diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs
index c3e24849c..e0a6fdd05 100644
--- a/juniper_codegen/src/graphql_interface/mod.rs
+++ b/juniper_codegen/src/graphql_interface/mod.rs
@@ -141,8 +141,8 @@ struct TraitAttr {
 
     /// Explicitly specified rule for tracing of fields that belong to this [GraphQL interface][1].
     ///
-    /// If absent and `tracing` feature enabled all fields not marked with `#[tracing(no_trace)]`
-    /// will be traced.
+    /// If absent and `tracing` feature enabled all fields not marked with
+    /// `#[graphql(tracing(ignore))]` will be traced.
     ///
     /// If it present but feature `tracing` disabled it will cause compilation error.
     ///
@@ -418,7 +418,7 @@ pub(crate) struct Definition {
     /// Description of this [GraphQL interface][1] to put into GraphQL schema.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    pub(crate) description: Option<String>,
+    description: Option<String>,
 
     /// Rust type of [`Context`] to generate [`GraphQLType`] implementation with
     /// for this [GraphQL interface][1].
@@ -426,7 +426,7 @@ pub(crate) struct Definition {
     /// [`GraphQLType`]: juniper::GraphQLType
     /// [`Context`]: juniper::Context
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
-    pub(crate) context: syn::Type,
+    context: syn::Type,
 
     /// [`ScalarValue`] parametrization to generate [`GraphQLType`]
     /// implementation with for this [GraphQL interface][1].
@@ -440,7 +440,7 @@ pub(crate) struct Definition {
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
-    pub(crate) fields: Vec<field::Definition>,
+    fields: Vec<field::Definition>,
 
     /// Defined [`Implementer`]s of this [GraphQL interface][1].
     ///
@@ -451,7 +451,7 @@ pub(crate) struct Definition {
     /// [GraphQL interface][1].
     ///
     /// If it's absent and `tracing` feature is enabled all [field][2]s not marked
-    /// with `#[tracing(no_trace)]` will be traced.
+    /// with `#[graphql(tracing(ignore))]` will be traced.
     ///
     /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
     /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
@@ -905,6 +905,7 @@ impl Implementer {
         let scalar = &self.scalar;
 
         let downcast = self.downcast_call_tokens(trait_ty, None);
+
         let resolving_code = gen::async_resolving_code(None, quote!());
 
         Some(quote! {
diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs
index ef767cff3..6170dc48b 100644
--- a/juniper_codegen/src/graphql_object/mod.rs
+++ b/juniper_codegen/src/graphql_object/mod.rs
@@ -272,8 +272,8 @@ pub(crate) struct Definition<Operation: ?Sized> {
     /// Explicitly specified rule, that used to define which [`GraphQL field`][1]s
     /// of this [`GraphQL object`][2] should be traced.
     ///
-    /// [1]: https://spec.graphql.org/June2018/#sec-Objects
-    /// [2]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
+    /// [2]: https://spec.graphql.org/June2018/#sec-Objects
     #[cfg(feature = "tracing")]
     pub(crate) tracing: tracing::Rule,
 }
diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs
index aa069215b..6b4205493 100644
--- a/juniper_codegen/src/lib.rs
+++ b/juniper_codegen/src/lib.rs
@@ -7,30 +7,6 @@
 #![doc(html_root_url = "https://docs.rs/juniper_codegen/0.15.7")]
 #![recursion_limit = "1024"]
 
-use proc_macro::TokenStream;
-
-use proc_macro_error::{proc_macro_error, ResultExt as _};
-use result::GraphQLScope;
-
-// NOTICE: Unfortunately this macro MUST be defined here, in the crate's root module, because Rust
-//         doesn't allow to export `macro_rules!` macros from a `proc-macro` crate type currently,
-//         and so we cannot move the definition into a sub-module and use the `#[macro_export]`
-//         attribute. Also it should be declared before `util` mod because `util` relies on it.
-/// Expands to `$expr` if `tracing` feature of this crate is enabled, otherwise returns an
-/// empty [`TokenStream`].
-macro_rules! if_tracing_enabled {
-    ($code: expr) => {{
-        #[cfg(feature = "tracing")]
-        {
-            $code
-        }
-        #[cfg(not(feature = "tracing"))]
-        {
-            ::quote::quote!()
-        }
-    }};
-}
-
 mod result;
 mod util;
 
@@ -143,6 +119,10 @@ mod graphql_union;
 #[cfg(feature = "tracing")]
 mod tracing;
 
+use proc_macro::TokenStream;
+use proc_macro_error::{proc_macro_error, ResultExt as _};
+use result::GraphQLScope;
+
 #[proc_macro_error]
 #[proc_macro_derive(GraphQLEnum, attributes(graphql))]
 pub fn derive_enum(input: TokenStream) -> TokenStream {
@@ -817,7 +797,14 @@ pub fn graphql_interface(attr: TokenStream, body: TokenStream) -> TokenStream {
 /// [`ScalarValue`]: juniper::ScalarValue
 /// [1]: https://spec.graphql.org/June2018/#sec-Objects
 #[proc_macro_error]
-#[proc_macro_derive(GraphQLObject, attributes(graphql, instrument, tracing))]
+#[cfg_attr(
+    feature = "tracing",
+    proc_macro_derive(GraphQLObject, attributes(graphql, instrument, tracing))
+)]
+#[cfg_attr(
+    not(feature = "tracing"),
+    proc_macro_derive(GraphQLObject, attributes(graphql))
+)]
 pub fn derive_object(body: TokenStream) -> TokenStream {
     self::graphql_object::derive::expand(body.into())
         .unwrap_or_abort()
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index 7920f1910..3132ae195 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -301,7 +301,7 @@ pub enum Rule {
     /// Trace all fields that can be synchronously resolved.
     Sync,
 
-    /// Trace only fields that marked with `#[tracing(only)]`.
+    /// Trace only fields that marked with `#[graphql(tracing(only))]`.
     Only,
 
     /// Skip tracing of all fields.
@@ -395,7 +395,7 @@ impl FieldBehavior {
 
 /// Generalisation of type that can be traced.
 pub trait TracedType {
-    /// Optional [`Rule`] read from attributes `#[tracing(...)]` object or interface
+    /// Optional [`Rule`] read from attributes `#[graphql(tracing(...))]` object or interface
     /// definition.
     fn tracing_rule(&self) -> Rule;
 
@@ -411,7 +411,7 @@ pub trait TracedField {
     /// Type of argument used by this field.
     type Arg: TracedArgument;
 
-    /// Returns parsed `#[tracing]` attribute.
+    /// Returns `#[instrument]` attribute, parsed from field resolver definition.
     fn instrument(&self) -> Option<&Attr>;
 
     /// Returns [`FieldBehaviour`] parsed from `#[graphql(tracing(...))]`
@@ -556,14 +556,30 @@ pub fn record_err_async(ty: &impl TracedType, field: &impl TracedField) -> Token
     )
 }
 
+/// Returns code to start tracing of a [GraphQL subscription][1].
+///
+/// [1]: https://spec.graphql.org/June2018/#sec-Subscription
+pub fn stream_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+    if !is_traced(ty, field) {
+        return quote!();
+    }
+    quote! (
+        let stream = <_ as ::juniper::tracing_futures::Instrument>::instrument(
+            stream,
+            _tracing_span,
+        );
+    )
+}
+
 /// Returns code to start tracing of a single iteration within `Stream`, unlike
 /// simple resolvers subscriptions have two layers of `Span`s, one to the whole
 /// `Stream` that represents this subscription and the second one is for individual
 /// resolvers.
-pub fn stream_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
+pub fn stream_next_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field) {
         return quote!();
     }
+
     // Sub span should have same level.
     let level = field
         .instrument()
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 229cf952d..68ceb6969 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -617,8 +617,6 @@ impl FieldAttributes {
 #[derive(Debug)]
 pub struct GraphQLTypeDefinitionFieldArg {
     pub name: String,
-    pub raw_name: syn::Ident,
-    pub resolver_code: TokenStream,
     pub description: Option<String>,
     pub default: Option<syn::Expr>,
     pub _type: Box<syn::Type>,
@@ -1128,9 +1126,8 @@ impl GraphQLTypeDefinition {
 
 #[cfg(test)]
 mod test {
-    use syn::{Ident, LitStr};
-
     use super::*;
+    use syn::{Ident, LitStr};
 
     fn strs_to_strings(source: Vec<&str>) -> Vec<String> {
         source

From 4861c24ae484383180d441befe304e8502db4785 Mon Sep 17 00:00:00 2001
From: Arsile <arsile410@gmail.com>
Date: Tue, 31 Aug 2021 15:06:06 +0300
Subject: [PATCH 72/72] Small corrections [run-ci]

---
 docs/book/content/tracing/index.md   | 38 +++++++++++++++-------------
 examples/tracing_support/src/main.rs |  1 -
 juniper/src/tests/tracing_tests.rs   |  6 +++--
 juniper_codegen/src/tracing.rs       | 12 +++++----
 4 files changed, 32 insertions(+), 25 deletions(-)

diff --git a/docs/book/content/tracing/index.md b/docs/book/content/tracing/index.md
index e59b816f9..3273823c2 100644
--- a/docs/book/content/tracing/index.md
+++ b/docs/book/content/tracing/index.md
@@ -43,9 +43,10 @@ impl Foo {
         self.value * another
     }
     
-    // Squaring is also hard but we don't know the value we squared so we can
-    // validate the results. In this case we can use `fields` argument to pass
-    // additional fields, that also will be included in span.
+    // Squaring is also hard, and for the scientific needs we're interested in
+    // the value that was squared so we should record it. In this case we can
+    // use `fields` argument to pass additional fields, that also will be
+    // included in span.
     #[instrument(fields(self.value = self.value))]
     fn square_value(&self) -> i32 {
         self.value * self.value
@@ -104,28 +105,28 @@ async fn main() {
 Juniper has it's own `#[instrument]` attribute, that can only be used with
 GraphQL Objects and Interfaces. In most aspects it mimics behavior of the original
 `#[instrument]` attribute from [tracing] crate including fields, sigils, so you
-could use it as a reference. First key deference you should keep in mind, `#[instrument]`
+could use it as a reference. First key deference you should keep in mind: `#[instrument]`
 applied implicitly to **all** resolvers if the `tracing` feature is enabled.
 Second and most significant difference is generated [`Span`]s. To fully understand
 this you should know how GraphQL Objects/Interfaces are actually resolved under
 the hood, long story short there is a lot of generated code and two methods
 `resolve_field` and `resolve_field_async` that map your resolvers to fields,
 and then recursively resolve fields of returned value (if it's not a `Scalar`).
-`#[instrument]` from [tracing] knows nothing about Juniper and  this, so it will
-only wrap method in [`Span`], ignoring recursive part capturing tip of the
-iceberg, so this will lead to plain sequence of resolver [`Span`]s, where you
-could hardly understand order and relations between each  resolver. On the other
-hand `#[instrument]` from Juniper is part of top-level macro so it's aware of
-all tricks performed by Juniper and will be expanded as part of `resolve_field`
-or `resolve_field_async`, [`Span`]s generated this way will capture full lifespan
-of value, including how it's fields resolved which results in more tree-like
-[`Span`] structure where you could easily navigate through, using tools like
+`#[instrument]` from [tracing] knows nothing about Juniper and all dark magic
+performed, so it will only wrap method in [`Span`], ignoring recursive part,
+effectively capturing only tip of the iceberg, so this will lead to plain sequence
+of resolver [`Span`]s, where you could hardly understand order and relations between
+each  resolver. On the other hand `#[instrument]` from Juniper is part of top-level
+macro so it's aware of all tricks performed by Juniper and will be expanded as part
+of `resolve_field` or `resolve_field_async`, [`Span`]s generated this way will capture
+full lifespan of value, including how it's fields resolved which results in more tree-like
+[`Span`] structure in which you could easily navigate through, using tools like
 [Jaeger]. As a bonus you'll get [`Span`] names, which refer to your schema instead
 of code.
 
 ## Skipping field resolvers
 
-In certain scenarios you may want to skip tracing of some fields because it too
+In certain scenarios you may want to skip tracing of some fields because it's too
 simple and straight-forward, that tracing preparations of this resolver would actually
 take more time then execution. In this cases you can use `tracing(ignore)` argument of
 `#[graphql]` attribute to completely disable tracing of this field resolver.
@@ -146,11 +147,13 @@ struct User {
 
 #[graphql_object(context = Context)]
 impl User {
+    // This won't produce span because it's marked with `tracing(ignore)`
     #[graphql(tracing(ignore))]
     fn id(&self) -> i32 {
         self.id
     }
 
+    // This will still produce span
     async fn friends(&self, context: &Context) -> Vec<User> {
         // Some async query in which you're actually interested.
 #       unimplemented!()
@@ -159,7 +162,7 @@ impl User {
 ```
 
 Manually setting `#[graphql(tracing(ignore))]` to skip tracing of all, let's
-say for example synchronous field resolvers is rather inefficient when you have 
+say for example, synchronous field resolvers is rather inefficient when you have 
 GraphQL object with too much fields. In this case you can use `tracing` attribute
 on top-level to trace specific group of fields or not to trace at all.
 `tracing` attribute can be used with one of the following arguments:
@@ -276,7 +279,7 @@ fn my_query(&self, non_debug: NonDebug) -> i32 {
 
 Custom fields generated this way are aware of `self` and can use `self` even if it not implicitly passed
 to resolver. In case when resolver is `fn` with not only `self` arguments they're also available
-to interact with as shown above. You can also access `executor` and `Context` as a result.
+to interact with as shown above. You can also access `executor` and a `Context` as a result.
 
 ### Example
 ```rust
@@ -351,7 +354,8 @@ async fn override_field() -> i32 {
     // In cases when you want to record a non-standard value to span you may
     // use `field::debug(...)` and `field::display(...)`, to set proper formatting.
     tracing::Span::current().record("msg", &field::debug(&Foo));
-    // Doing `tracing_span.record("msg", Foo)` will result in compile error.
+    // Doing `tracing::Span::current().record("msg", Foo)` will result in
+    // compilation error.
 #   unimplemented!()
 }
 # }
diff --git a/examples/tracing_support/src/main.rs b/examples/tracing_support/src/main.rs
index d923a0f4f..e651ac7ed 100644
--- a/examples/tracing_support/src/main.rs
+++ b/examples/tracing_support/src/main.rs
@@ -110,7 +110,6 @@ impl Query {
         }
     }
 
-    /// Create guest user with the given `id` and `name`.
     #[instrument(skip(id))] // Here we skip `id` from being recorded into spans fields
     fn guest(id: i32, name: String) -> User {
         User {
diff --git a/juniper/src/tests/tracing_tests.rs b/juniper/src/tests/tracing_tests.rs
index e588b4ded..9f1b9a60d 100644
--- a/juniper/src/tests/tracing_tests.rs
+++ b/juniper/src/tests/tracing_tests.rs
@@ -408,7 +408,8 @@ async fn graphql_object_trace_arg() {
         .enter_new_span("Query.derivedSync")
         .simple_span("SyncDerived.sync")
         .close_exited("Query.derivedSync")
-        // There shouldn't be any spans because `SkipAll` and `SkipAllDerived` marked with "skip-all"
+        // There shouldn't be any spans because `SkipAll` and `SkipAllDerived`
+        // marked with "skip-all"
         .simple_span("Query.skipAll")
         .simple_span("Query.skipAllDerived")
         .enter_new_span("Query.onlySync")
@@ -423,7 +424,8 @@ async fn graphql_object_trace_arg() {
                 .with_field("test", "\"magic\"")
                 .with_strict_fields(true),
         )
-        // There shouldn't be span for `sync` because it's not marked with `#[tracing(only)]`
+        // There shouldn't be span for `sync` because it's not marked with
+        // `#[graphql(tracing(only))]`
         .close_exited("Query.onlyDerived")
         .close_exited("execute_validated_query_async")
         .close_exited("execute");
diff --git a/juniper_codegen/src/tracing.rs b/juniper_codegen/src/tracing.rs
index 3132ae195..3172b8034 100644
--- a/juniper_codegen/src/tracing.rs
+++ b/juniper_codegen/src/tracing.rs
@@ -147,7 +147,7 @@ impl Parse for Attr {
     }
 }
 
-/// Custom field that should be recorded in span, explicitly defined by user.
+/// Custom field that should be recorded in span, explicitly specified by user.
 #[derive(Clone, Debug)]
 pub struct Field {
     /// Left part of this [`Field`], represents name of recorded field.
@@ -352,7 +352,7 @@ impl Parse for Rule {
                 ident.span(),
                 format!(
                     "Unknown tracing rule: {}, \
-                         known values: sync, async, skip-all and complex",
+                     known values: `sync`, `async`, `skip_all` and `only`.",
                     tracing,
                 ),
             )),
@@ -395,8 +395,8 @@ impl FieldBehavior {
 
 /// Generalisation of type that can be traced.
 pub trait TracedType {
-    /// Optional [`Rule`] read from attributes `#[graphql(tracing(...))]` object or interface
-    /// definition.
+    /// Optional [`Rule`] read from attributes `#[graphql(tracing(...))]` object
+    /// or interface definition.
     fn tracing_rule(&self) -> Rule;
 
     /// Name of this type.
@@ -406,7 +406,7 @@ pub trait TracedType {
     fn scalar(&self) -> Option<syn::Type>;
 }
 
-/// Trait that marks type that this is field that can be traced.
+/// Trait that marks type of field that can be traced.
 pub trait TracedField {
     /// Type of argument used by this field.
     type Arg: TracedArgument;
@@ -609,6 +609,7 @@ pub fn stream_next_tokens(ty: &impl TracedType, field: &impl TracedField) -> Tok
     )
 }
 
+/// Returns code that will record error within subscription.
 pub fn record_err_stream(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field)
         || !field
@@ -635,6 +636,7 @@ pub fn sync_tokens(ty: &impl TracedType, field: &impl TracedField) -> TokenStrea
     quote!(let _tracing_guard = _tracing_span.enter();)
 }
 
+/// Returns code that will record error within sync code block.
 pub fn record_err_sync(ty: &impl TracedType, field: &impl TracedField) -> TokenStream {
     if !is_traced(ty, field)
         || !field