tokio/tokio-trace/tests/subscriber.rs
Eliza Weisman b8f63308d7
trace-core: Pass dispatcher by ref to dispatcher::with_default (#971)
* trace-core: Pass dispatcher by ref to `dispatcher::with_default`

As requested by @carllerche in https://github.com/tokio-rs/tokio/pull/966#discussion_r264380005, this branch changes the
`dispatcher::with_default` function in `tokio-trace-core` to take the
dispatcher by ref and perform the clone internally. This makes this
function more consistant with other `with_default` functions in other
crates.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>

* trace: Don't set the default dispatcher on entering a span

Setting the default dispatcher on span entry is a relic of when spans
tracked their parent's ID. At that time, it was necessary to ensure that
any spans created inside a span were observed by the same subscriber
that originally provided the entered span with an ID, as otherwise, new
spans would be created with parent IDs that did not originate from that
subscriber.

Now that spans don't track their parent ID, this is no longer necessary.
However, removing this behavior does mean that if a span is entered
outside of the subscriber context it was created in, any subsequent
spans will be observed by the current default subscriber and thus will
not be part of the original span's trace tree. Since subscribers are not
expected to change frequently, and spans are not expected to move
between them, this is likely acceptable.

I've removed the tests for the old behavior.

Note that this change improves the performance of span entry/exit fairly
significantly. Here are the results of running a benchmark that enters
a span, does nothing, and immediately exits it, before this change:

```
test enter_span              ... bench:          93 ns/iter (+/- 14)
```

...and after:

```
test enter_span              ... bench:          51 ns/iter (+/- 9)
```

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
2019-03-11 15:29:00 -07:00

171 lines
5.4 KiB
Rust

#[macro_use]
extern crate tokio_trace;
mod support;
use self::support::*;
use tokio_trace::subscriber::with_default;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
#[test]
fn filters_are_not_reevaluated_for_the_same_span() {
// Asserts that the `span!` macro caches the result of calling
// `Subscriber::enabled` for each span.
let alice_count = Arc::new(AtomicUsize::new(0));
let bob_count = Arc::new(AtomicUsize::new(0));
let alice_count2 = alice_count.clone();
let bob_count2 = bob_count.clone();
let (subscriber, handle) = subscriber::mock()
.with_filter(move |meta| match meta.name {
"alice" => {
alice_count2.fetch_add(1, Ordering::Relaxed);
false
}
"bob" => {
bob_count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
})
.run_with_handle();
with_default(subscriber, move || {
// Enter "alice" and then "bob". The dispatcher expects to see "bob" but
// not "alice."
let mut alice = span!("alice");
let mut bob = alice.enter(|| {
let mut bob = span!("bob");
bob.enter(|| ());
bob
});
// The filter should have seen each span a single time.
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
alice.enter(|| bob.enter(|| {}));
// The subscriber should see "bob" again, but the filter should not have
// been called.
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
bob.enter(|| {});
assert_eq!(alice_count.load(Ordering::Relaxed), 1);
assert_eq!(bob_count.load(Ordering::Relaxed), 1);
});
handle.assert_finished();
}
#[test]
fn filters_are_reevaluated_for_different_call_sites() {
// Asserts that the `span!` macro caches the result of calling
// `Subscriber::enabled` for each span.
let charlie_count = Arc::new(AtomicUsize::new(0));
let dave_count = Arc::new(AtomicUsize::new(0));
let charlie_count2 = charlie_count.clone();
let dave_count2 = dave_count.clone();
let subscriber = subscriber::mock()
.with_filter(move |meta| {
println!("Filter: {:?}", meta.name);
match meta.name {
"charlie" => {
charlie_count2.fetch_add(1, Ordering::Relaxed);
false
}
"dave" => {
dave_count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
}
})
.run();
with_default(subscriber, move || {
// Enter "charlie" and then "dave". The dispatcher expects to see "dave" but
// not "charlie."
let mut charlie = span!("charlie");
let mut dave = charlie.enter(|| {
let mut dave = span!("dave");
dave.enter(|| {});
dave
});
// The filter should have seen each span a single time.
assert_eq!(charlie_count.load(Ordering::Relaxed), 1);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
charlie.enter(|| dave.enter(|| {}));
// The subscriber should see "dave" again, but the filter should not have
// been called.
assert_eq!(charlie_count.load(Ordering::Relaxed), 1);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
// A different span with the same name has a different call site, so it
// should cause the filter to be reapplied.
let mut charlie2 = span!("charlie");
charlie.enter(|| {});
assert_eq!(charlie_count.load(Ordering::Relaxed), 2);
assert_eq!(dave_count.load(Ordering::Relaxed), 1);
// But, the filter should not be re-evaluated for the new "charlie" span
// when it is re-entered.
charlie2.enter(|| span!("dave").enter(|| {}));
assert_eq!(charlie_count.load(Ordering::Relaxed), 2);
assert_eq!(dave_count.load(Ordering::Relaxed), 2);
});
}
#[test]
fn filter_caching_is_lexically_scoped() {
pub fn my_great_function() -> bool {
span!("emily").enter(|| true)
}
pub fn my_other_function() -> bool {
span!("frank").enter(|| true)
}
let count = Arc::new(AtomicUsize::new(0));
let count2 = count.clone();
let subscriber = subscriber::mock()
.with_filter(move |meta| match meta.name {
"emily" | "frank" => {
count2.fetch_add(1, Ordering::Relaxed);
true
}
_ => false,
})
.run();
with_default(subscriber, || {
// Call the function once. The filter should be re-evaluated.
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1);
// Call the function again. The cached result should be used.
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1);
assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
});
}