trace: Change Span::enter to return a guard, add Span::in_scope (#1076)

## Motivation

Currently, the primary way to use a span is to use `.enter` and pass a
closure to be executed under the span. While that is convenient in many
settings, it also comes with two decently inconvenient drawbacks:

 - It breaks control flow statements like `return`, `?`, `break`, and
   `continue`
 - It require re-indenting a potentially large chunk of code if you wish
   it to appear under a span

## Solution

This branch changes the `Span::enter` function to return a scope guard 
that exits the span when dropped, as in:
```rust
let guard = span.enter();

// code here is within the span

drop(guard);

// code here is no longer within the span
```
The method previously called `enter`, which takes a closure and 
executes it in the span's context, is now called `Span::in_scope`, and
was reimplemented on top of the new `enter` method. 

This is a breaking change to `tokio-trace` that will be part of the
upcoming 0.2 release.

Closes #1075 

Signed-off-by: Eliza Weisman <[email protected]>
This commit is contained in:
Eliza Weisman
2019-05-24 15:24:13 -07:00
committed by GitHub
parent 1b498e8aa2
commit 84d5a7f5a0
12 changed files with 284 additions and 131 deletions
+60 -45
View File
@@ -43,8 +43,22 @@
//! use tokio_trace::Level;
//!
//! # fn main() {
//! span!(Level::TRACE, "my_span").enter(|| {
//! // perform some work in the context of `my_span`...
//! let span = span!(Level::TRACE, "my_span");
//! let _enter = span.enter();
//! // perform some work in the context of `my_span`...
//! # }
//!```
//!
//! The [`in_scope`] method may be used to execute a closure inside a
//! span:
//!
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! # let span = span!(Level::TRACE, "my_span");
//! span.in_scope(|| {
//! // perform some more work in the context of `my_span`...
//! });
//! # }
//!```
@@ -61,13 +75,13 @@
//! # use tokio_trace::Level;
//! # fn main() {
//! // this span is considered the "root" of a new trace tree:
//! span!(Level::INFO, "root").enter(|| {
//! span!(Level::INFO, "root").in_scope(|| {
//! // since we are now inside "root", this span is considered a child
//! // of "root":
//! span!(Level::DEBUG, "outer_child").enter(|| {
//! span!(Level::DEBUG, "outer_child").in_scope(|| {
//! // this span is a child of "outer_child", which is in turn a
//! // child of "root":
//! span!(Level::TRACE, "inner_child").enter(|| {
//! span!(Level::TRACE, "inner_child").in_scope(|| {
//! // and so on...
//! });
//! });
@@ -143,12 +157,12 @@
//! # use tokio_trace::Level;
//! # fn main() {
//! # let n = 1;
//! span!(Level::TRACE, "my loop").enter(|| {
//! for i in 0..n {
//! # let _ = i;
//! // ...
//! }
//! })
//! let span = span!(Level::TRACE, "my_loop");
//! let _enter = span.enter();
//! for i in 0..n {
//! # let _ = i;
//! // ...
//! }
//! # }
//! ```
//! Or, should we create a new span for each iteration of the loop, as in:
@@ -158,10 +172,9 @@
//! # fn main() {
//! # let n = 1u64;
//! for i in 0..n {
//! # let _ = i;
//! span!(Level::TRACE, "my loop", iteration = i).enter(|| {
//! // ...
//! })
//! let span = span!(Level::TRACE, "my_loop", iteration = i);
//! let _enter = span.enter();
//! // ...
//! }
//! # }
//! ```
@@ -189,7 +202,7 @@
//! // records an event outside of any span context:
//! event!(Level::INFO, "something happened");
//!
//! span!(Level::INFO, "my_span").enter(|| {
//! span!(Level::INFO, "my_span").in_scope(|| {
//! // records an event within "my_span".
//! event!(Level::DEBUG, "something happened inside my_span");
//! });
@@ -257,11 +270,14 @@
//! # fn main() {
//! // Construct a new span named "my span" with trace log level.
//! let span = span!(Level::TRACE, "my span");
//! span.enter(|| {
//! // Any trace events in this closure or code called by it will occur within
//! // the span.
//! });
//! // Dropping the span will close it, indicating that it has ended.
//!
//! // Enter the span, returning a guard object.
//! let _enter = span.enter();
//!
//! // Any trace events that occur before the guard is dropped will occur
//! // within the span.
//!
//! // Dropping the guard will exit the span.
//! # }
//! ```
//!
@@ -295,32 +311,30 @@
//! # fn find_a_razor() -> Result<u32, u32> { Ok(1) }
//! # fn main() {
//! pub fn shave_the_yak(yak: &mut Yak) {
//! // Create a new span for this invocation of `shave_the_yak`, annotated
//! // with the yak being shaved as a *field* on the span.
//! span!(Level::TRACE, "shave_the_yak", yak = ?yak).enter(|| {
//! // Since the span is annotated with the yak, it is part of the context
//! // for everything happening inside the span. Therefore, we don't need
//! // to add it to the message for this event, as the `log` crate does.
//! info!(target: "yak_events", "Commencing yak shaving");
//! let span = span!(Level::TRACE, "shave_the_yak", yak = ?yak);
//! let _enter = span.enter();
//!
//! loop {
//! match find_a_razor() {
//! Ok(razor) => {
//! // We can add the razor as a field rather than formatting it
//! // as part of the message, allowing subscribers to consume it
//! // in a more structured manner:
//! info!({ razor = %razor }, "Razor located");
//! yak.shave(razor);
//! break;
//! }
//! Err(err) => {
//! // However, we can also create events with formatted messages,
//! // just as we would for log records.
//! warn!("Unable to locate a razor: {}, retrying", err);
//! }
//! // Since the span is annotated with the yak, it is part of the context
//! // for everything happening inside the span. Therefore, we don't need
//! // to add it to the message for this event, as the `log` crate does.
//! info!(target: "yak_events", "Commencing yak shaving");
//! loop {
//! match find_a_razor() {
//! Ok(razor) => {
//! // We can add the razor as a field rather than formatting it
//! // as part of the message, allowing subscribers to consume it
//! // in a more structured manner:
//! info!({ razor = %razor }, "Razor located");
//! yak.shave(razor);
//! break;
//! }
//! Err(err) => {
//! // However, we can also create events with formatted messages,
//! // just as we would for log records.
//! warn!("Unable to locate a razor: {}, retrying", err);
//! }
//! }
//! })
//! }
//! }
//! # }
//! ```
@@ -414,7 +428,8 @@
//! ```
//!
//! [`log`]: https://docs.rs/log/0.4.6/log/
//! [`Span`]: span/struct.Span
//! [`Span`]: span/struct.Span.html
//! [`in_scope`]: span/struct.Span.html#method.in_scope
//! [`Event`]: struct.Event.html
//! [`Subscriber`]: subscriber/trait.Subscriber.html
//! [`observe_event`]: subscriber/trait.Subscriber.html#tymethod.observe_event
+7 -7
View File
@@ -9,7 +9,7 @@
/// # use tokio_trace::Level;
/// # fn main() {
/// let span = span!(Level::TRACE, "my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -21,7 +21,7 @@
/// # extern crate tokio_trace;
/// # use tokio_trace::Level;
/// # fn main() {
/// span!(Level::TRACE, "my span", foo = 2, bar = "a string").enter(|| {
/// span!(Level::TRACE, "my span", foo = 2, bar = "a string").in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -240,7 +240,7 @@ macro_rules! span {
/// # extern crate tokio_trace;
/// # fn main() {
/// let span = trace_span!("my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -301,7 +301,7 @@ macro_rules! trace_span {
/// # extern crate tokio_trace;
/// # fn main() {
/// let span = debug_span!("my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -362,7 +362,7 @@ macro_rules! debug_span {
/// # extern crate tokio_trace;
/// # fn main() {
/// let span = info_span!("my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -423,7 +423,7 @@ macro_rules! info_span {
/// # extern crate tokio_trace;
/// # fn main() {
/// let span = warn_span!("my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
@@ -483,7 +483,7 @@ macro_rules! warn_span {
/// # extern crate tokio_trace;
/// # fn main() {
/// let span = error_span!("my span");
/// span.enter(|| {
/// span.in_scope(|| {
/// // do work inside the span...
/// });
/// # }
+155 -35
View File
@@ -4,12 +4,10 @@
//!
//! A thread of execution is said to _enter_ a span when it begins executing,
//! and _exit_ the span when it switches to another context. Spans may be
//! entered through the [`enter`] method, which enters the target span,
//! performs a given function (either a closure or a function pointer), exits
//! the span, and then returns the result.
//! entered through the [`enter`] and [`in_scope`] methods.
//!
//! Calling `enter` on a span handle enters the span that handle corresponds to,
//! if the span exists:
//! The `enter` method enters a span, returning a [guard] that exits the span
//! when dropped
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
@@ -17,18 +15,36 @@
//! let my_var: u64 = 5;
//! let my_span = span!(Level::TRACE, "my_span", my_var = &my_var);
//!
//! my_span.enter(|| {
//! // `my_span` exists but has not been entered.
//!
//! let _enter = my_span.enter();
//!
//! // Perform some work inside of the context of `my_span`...
//! # }
//!```
//!
//! `in_scope` takes a closure or function pointer and executes it inside the
//! span.
//! ```
//! # #[macro_use] extern crate tokio_trace;
//! # use tokio_trace::Level;
//! # fn main() {
//! let my_var: u64 = 5;
//! let my_span = span!(Level::TRACE, "my_span", my_var = &my_var);
//!
//! my_span.in_scope(|| {
//! // perform some work in the context of `my_span`...
//! });
//!
//! // Perform some work outside of the context of `my_span`...
//!
//! my_span.enter(|| {
//! my_span.in_scope(|| {
//! // Perform some more work in the context of `my_span`.
//! });
//! # }
//! ```
//!
//!
//! # The Span Lifecycle
//!
//! Execution may enter and exit a span multiple times before that
@@ -48,7 +64,7 @@
//! type Error = ();
//!
//! fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
//! self.span.enter(|| {
//! self.span.in_scope(|| {
//! // Do actual future work
//! # Ok(Async::Ready(()))
//! })
@@ -81,7 +97,7 @@
//! # use tokio_trace::Level;
//! # fn main() {
//! {
//! span!(Level::TRACE, "my_span").enter(|| {
//! span!(Level::TRACE, "my_span").in_scope(|| {
//! // perform some work in the context of `my_span`...
//! }); // --> Subscriber::exit(my_span)
//!
@@ -116,6 +132,8 @@
//! [`Subscriber`]: ../subscriber/trait.Subscriber.html
//! [`Attributes`]: struct.Attributes.html
//! [`enter`]: struct.Span.html#method.enter
//! [`in_scope`]: struct.Span.html#method.in_scope
//! [`guard`]: struct.Entered.html
pub use tokio_trace_core::span::{Attributes, Id, Record};
use std::{
@@ -166,15 +184,15 @@ pub(crate) struct Inner {
/// A guard representing a span which has been entered and is currently
/// executing.
///
/// This guard may be used to exit the span, returning an `Enter` to
/// re-enter it.
/// When the guard is dropped, the span will be exited.
///
/// This type is primarily used for implementing span handles; users should
/// typically not need to interact with it directly.
/// This is returned by the [`Span::enter`] function.
///
/// [`Span::enter`]: ../struct.Span.html#method.enter
#[derive(Debug)]
#[must_use = "once a span has been entered, it should be exited"]
struct Entered<'a> {
inner: &'a Inner,
pub struct Entered<'a> {
span: &'a Span,
}
// ===== impl Span =====
@@ -254,20 +272,131 @@ impl Span {
span
}
/// Enters this span, returning a guard that will exit the span when dropped.
///
/// If this span is enabled by the current subscriber, then this function will
/// call [`Subscriber::enter`] with the span's [`Id`], and dropping the guard
/// will call [`Subscriber::exit`]. If the span is disabled, this does nothing.
///
/// # Examples
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// # fn main() {
/// let span = span!(Level::INFO, "my_span");
/// let guard = span.enter();
///
/// // code here is within the span
///
/// drop(guard);
///
/// // code here is no longer within the span
///
/// # }
/// ```
///
/// Guards need not be explicitly dropped:
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # fn main() {
/// fn my_function() -> String {
/// // enter a span for the duration of this function.
/// let span = trace_span!("my_function");
/// let _enter = span.enter();
///
/// // anything happening in functions we call is still inside the span...
/// my_other_function();
///
/// // returning from the function drops the guard, exiting the span.
/// return "Hello world".to_owned();
/// }
///
/// fn my_other_function() {
/// // ...
/// }
/// # }
/// ```
///
/// Sub-scopes may be created to limit the duration for which the span is
/// entered:
///
/// ```
/// #[macro_use] extern crate tokio_trace;
/// # fn main() {
/// let span = info_span!("my_great_span");
///
/// {
/// let _enter = span.enter();
///
/// // this event occurs inside the span.
/// info!("i'm in the span!");
///
/// // exiting the scope drops the guard, exiting the span.
/// }
///
/// // this event is not inside the span.
/// info!("i'm outside the span!")
/// # }
/// ```
///
/// [`Subscriber::enter`]: ../subscriber/trait.Subscriber.html#method.enter
/// [`Subscriber::exit`]: ../subscriber/trait.Subscriber.html#method.exit
/// [`Id`]: ../struct.Id.html
pub fn enter<'a>(&'a self) -> Entered<'a> {
if let Some(ref inner) = self.inner.as_ref() {
inner.subscriber.enter(&inner.id);
}
self.log(format_args!("-> {}", self.meta.name));
Entered { span: self }
}
/// Executes the given function in the context of this span.
///
/// If this span is enabled, then this function enters the span, invokes
/// If this span is enabled, then this function enters the span, invokes `f`
/// and then exits the span. If the span is disabled, `f` will still be
/// invoked, but in the context of the currently-executing span (if there is
/// one).
///
/// Returns the result of evaluating `f`.
pub fn enter<F: FnOnce() -> T, T>(&self, f: F) -> T {
self.log(format_args!("-> {}", self.meta.name));
let _enter = self.inner.as_ref().map(Inner::enter);
let result = f();
self.log(format_args!("<- {}", self.meta.name));
result
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// # fn main() {
/// let my_span = span!(Level::TRACE, "my_span");
///
/// my_span.in_scope(|| {
/// // this event occurs within the span.
/// trace!("i'm in the span!");
/// });
///
/// // this event occurs outside the span.
/// trace!("i'm not in the span!");
/// # }
/// ```
///
/// Calling a function and returning the result:
/// ```
/// # #[macro_use] extern crate tokio_trace;
/// # use tokio_trace::Level;
/// fn hello_world() -> String {
/// "Hello world!".to_owned()
/// }
///
/// # fn main() {
/// let span = info_span!("hello_world");
/// // the span will be entered for the duration of the call to
/// // `hello_world`.
/// let a_string = span.in_scope(hello_world);
/// # }
///
pub fn in_scope<F: FnOnce() -> T, T>(&self, f: F) -> T {
let _enter = self.enter();
f()
}
/// Returns a [`Field`](../field/struct.Field.html) for the field with the
@@ -435,18 +564,6 @@ impl fmt::Debug for Span {
// ===== impl Inner =====
impl Inner {
/// Enters the span, returning a guard that may be used to exit the span and
/// re-enter the prior span.
///
/// This is used internally to implement `Span::enter`. It may be used for
/// writing custom span handles, but should generally not be called directly
/// when entering a span.
#[inline]
fn enter(&self) -> Entered {
self.subscriber.enter(&self.id);
Entered { inner: self }
}
/// Indicates that the span with the given ID has an indirect causal
/// relationship with this span.
///
@@ -519,7 +636,10 @@ impl<'a> Drop for Entered<'a> {
//
// Running this behaviour on drop rather than with an explicit function
// call means that spans may still be exited when unwinding.
self.inner.subscriber.exit(&self.inner.id);
if let Some(inner) = self.span.inner.as_ref() {
inner.subscriber.exit(&inner.id);
}
self.span.log(format_args!("<- {}", self.span.meta.name));
}
}