-
Notifications
You must be signed in to change notification settings - Fork 434
Add tracing support for GraphQL objects, interfaces and subscriptions #972
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
ArsileLuci
wants to merge
91
commits into
graphql-rust:master
Choose a base branch
from
ArsileLuci:tracing-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 84 commits
Commits
Show all changes
91 commits
Select commit
Hold shift + click to select a range
e0f7eb5
Add tracing support
mihai-dinculescu 419520d
Merge branch 'master' into add_tracing_support
LegNeato c1f9e5f
Merge branch 'master' into add_tracing_support
LegNeato 753c80f
Rocket can now compile on stable
LegNeato c599bae
Remove `boxed` in favor of `pin`.
LegNeato b556934
Add tracing support example
LegNeato 0134da3
Add some coarse execution tracing
LegNeato 5c888a8
Remove old comment / docs
LegNeato ef2da9b
Merge branch 'master' into add_tracing_support
LegNeato 01b3453
Fix book tests
LegNeato 7ac7304
fix up some imports
LegNeato 06687dd
Add back subscriber Cargo.toml instructions
LegNeato 697a743
Change trace errors to trace
mihai-dinculescu f2977d5
Tracing unit tests
mihai-dinculescu 6c3654c
Tracing unit tests names
mihai-dinculescu 7f746de
Revert "Rocket can now compile on stable"
LegNeato 64cad08
Merge branch 'master' into add_tracing_support
LegNeato d7acba9
Merge branch 'master' into add_tracing_support
LegNeato 88dc9c4
Fix tracing spans
LegNeato 8a2f6ec
Do not include trailing newline on the last error
LegNeato 83fd44d
Standard tracing labels on snake_case
LegNeato c92dd87
Add a validation error case to the tracing example
LegNeato 64d8e11
Use $crate everywhere
LegNeato cdbc0ce
Add other levels to span macros
LegNeato 65ab688
Merge branch 'master' into add_tracing_support
LegNeato 6cf48d9
Merge branch 'master' into add_tracing_support
LegNeato 5f8a4ca
Merge branch 'master' into add_tracing_support
LegNeato ae1054e
Merge branch 'master' into add_tracing_support
LegNeato 5938345
Use the released tracing crate
mihai-dinculescu 8bc07f6
Fix email address
LegNeato 82497d3
Standardize on snake case for tracing
LegNeato 79102a3
Add tracing support
mihai-dinculescu 6a16cf8
Rocket can now compile on stable
LegNeato 11bb5e2
Add tracing support example
LegNeato 57ecb56
Add some coarse execution tracing
LegNeato cff6678
Remove old comment / docs
LegNeato cb3ee27
Fix book tests
LegNeato 0d52c50
fix up some imports
LegNeato 7f24509
Add back subscriber Cargo.toml instructions
LegNeato cb7ab0c
Change trace errors to trace
mihai-dinculescu 7b02ccc
Tracing unit tests
mihai-dinculescu 4000f1e
Tracing unit tests names
mihai-dinculescu e968c4b
Revert "Rocket can now compile on stable"
LegNeato 58fed76
Fix tracing spans
LegNeato 7b5ad7e
Do not include trailing newline on the last error
LegNeato c34a1c2
Standard tracing labels on snake_case
LegNeato 26407e2
Add a validation error case to the tracing example
LegNeato 94279d1
Use $crate everywhere
LegNeato bf35077
Add other levels to span macros
LegNeato 2365016
Use the released tracing crate
mihai-dinculescu ca744b5
Fix email address
LegNeato 62acd3f
Standardize on snake case for tracing
LegNeato 4d7797a
Merge master
LegNeato 72a28d4
Remove rustfmt option causing breakage
LegNeato 2977b1f
Remove subscription helper mod
LegNeato 264030f
Merge branch 'master' into add_tracing_support
LegNeato 7a1dfdc
Fix type checking on fragments and implement e2e tests to cover this …
ArsileLuci d427f80
Add CHANGELOG note
ArsileLuci a34294c
Merge branch 'master' into ArsileLuci-master
tyranron 64cb389
Some corrections [skip ci]
tyranron a340673
Fix interface fields not being resolved for sync context and rework a…
ArsileLuci 28ebd31
Minor corrections
ArsileLuci 9e46dce
Small style corrections
tyranron 7c6fe5c
Merge master
ArsileLuci 72d8cfa
Merge master
ArsileLuci d57126a
Merge master
ArsileLuci d0558a4
Impl async resolve tracing
ArsileLuci 3c555dd
Impl codegen, add initial docs and tests
ArsileLuci 5b1fed8
Fully hide tracing behind feature gate, add docs
ArsileLuci 05a1432
Add test for subscriptions
ArsileLuci 174593e
Extend example, book and small test corrections [run ci]
ArsileLuci 469a46e
Merge branch 'master' into tracing-support
ArsileLuci 2fe4106
Fix book imports [run ci]
ArsileLuci bb0e87d
Merge branch 'tracing-support' of https://github.com/ArsileLuci/junip…
ArsileLuci 7b9ac11
Fix tokio dependency in book [run ci]
ArsileLuci 2772b32
Fix book build [run ci]
ArsileLuci b2a285a
Minor corrections [run ci]
ArsileLuci 33d6952
Some corrections
tyranron 1af4115
WIP
ArsileLuci 46c76bc
Refactor
ArsileLuci 5dbdc94
Book corrections
ArsileLuci 07bae4b
Fetch upstream
ArsileLuci c256a2e
Restore tracing codegen
ArsileLuci 123a87b
Book and docs correction
ArsileLuci 3420e8f
fmt fix
ArsileLuci 7e31803
Impl sigils
ArsileLuci ae02db7
Impl field::Empty
ArsileLuci e69f261
Improve subscription tracing [run ci]
ArsileLuci c22ef43
Remove redundant code
ArsileLuci a2e43fc
Merge branch 'graphql-rust:master' into tracing-support
ArsileLuci 4861c24
Small corrections [run-ci]
ArsileLuci File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
# Tracing | ||
|
||
Juniper has optional support for the [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.15.7", features = ["default", "tracing"]} | ||
tracing = "0.1.26" | ||
tracing-subscriber = "0.2.15" | ||
``` | ||
|
||
## Usage | ||
|
||
```rust | ||
# extern crate juniper; | ||
extern crate tokio; | ||
extern crate tracing; | ||
extern crate tracing_subscriber; | ||
use juniper::{EmptyMutation, EmptySubscription, RootNode, graphql_object, Variables}; | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
struct Query; | ||
|
||
struct Foo { | ||
value: i32, | ||
} | ||
|
||
#[graphql_object] | ||
impl Foo { | ||
// Value resolving is pretty straight-forward so we can skip tracing. | ||
#[graphql(tracing(ignore))] | ||
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_values(&self, another: i32) -> i32 { | ||
self.value * another | ||
} | ||
|
||
// Here we'll record span and it will have field with name "self.value" | ||
#[instrument(fields(self.value = self.value))] | ||
fn square_value(&self) -> i32 { | ||
self.value * self.value | ||
} | ||
} | ||
|
||
#[graphql_object] | ||
impl Query { | ||
async fn foo() -> Foo { | ||
Foo { value: 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 = r#" | ||
{ | ||
foo { | ||
value | ||
multiplyValues(another: 23) | ||
squareValue | ||
} | ||
}"#; | ||
let (_, _errors) = juniper::execute(query, None, &root, &vars, &()) | ||
.await | ||
.unwrap(); | ||
} | ||
``` | ||
|
||
## Skipping field resolvers | ||
|
||
In certain scenarios you may want to skip tracing of some fields because it 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. | ||
|
||
### Example | ||
|
||
```rust | ||
# extern crate juniper; | ||
# use juniper::graphql_object; | ||
# fn main() {} | ||
# | ||
# struct Context; | ||
# impl juniper::Context for Context {} | ||
|
||
struct User { | ||
id: i32, | ||
} | ||
|
||
#[graphql_object(context = Context)] | ||
impl User { | ||
#[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. | ||
# unimplemented!() | ||
} | ||
} | ||
``` | ||
|
||
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. | ||
`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 `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. | ||
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 | ||
`Context`). | ||
|
||
```rust | ||
# extern crate juniper; | ||
# use juniper::{graphql_object, GraphQLObject}; | ||
# | ||
# fn main() {} | ||
# | ||
# 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!() | ||
} | ||
} | ||
``` | ||
|
||
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 | ||
```rust | ||
# extern crate juniper; | ||
# use juniper::graphql_object; | ||
# | ||
# fn main() {} | ||
# | ||
# struct Query; | ||
|
||
#[derive(Clone, juniper::GraphQLInputObject)] | ||
struct NonDebug { | ||
important_field: String, | ||
} | ||
|
||
# #[graphql_object] | ||
# impl Query { | ||
// 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 { | ||
// Some query | ||
# unimplemented!() | ||
} | ||
# } | ||
``` | ||
|
||
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 | ||
|
||
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. | ||
|
||
[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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ authors = ["Magnus Hallin <[email protected]>"] | |
build = "build.rs" | ||
|
||
[dependencies] | ||
juniper = { path = "../../../juniper" } | ||
juniper = { path = "../../../juniper", features = ["default", "tracing"] } | ||
juniper_iron = { path = "../../../juniper_iron" } | ||
juniper_subscriptions = { path = "../../../juniper_subscriptions" } | ||
|
||
|
@@ -16,9 +16,12 @@ 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", "rt-multi-thread"] } | ||
uuid = "0.8" | ||
|
||
tracing = "0.1.26" | ||
tracing-subscriber = "0.2.1" | ||
|
||
[build-dependencies] | ||
skeptic = "0.13" | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/target/ | ||
**/*.rs.bk | ||
Cargo.lock | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.