Skip to content

Commit

Permalink
subscriber: improve docs on Layer composition (#566)
Browse files Browse the repository at this point in the history
Depends on #564 

This commit adds a new section to the `Layer` docs on how `Layer`s and
`Subscriber`s are composed. Additionally, it updates the docs to prefer
the use of `SubscriberExt::with` over `Layer::with_subscriber`.

Additionally, I've fixed how `SubscriberExt::with` is implemented, so
that `Layer`s may use `with_subscriber` as a callback for composition.

Closes  #452
Closes #505 (IMO)
  • Loading branch information
hawkw authored Feb 4, 2020
1 parent a661155 commit e32012a
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 17 deletions.
4 changes: 2 additions & 2 deletions tracing-subscriber/src/fmt/fmt_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use tracing_core::{
/// use tracing_subscriber::{fmt, registry::Registry};
/// use tracing_subscriber::prelude::*;
///
/// let subscriber = fmt::Layer::default()
/// .with_subscriber(Registry::default());
/// let subscriber = Registry::default()
/// .with(fmt::Layer::default());
///
/// tracing::subscriber::set_global_default(subscriber).unwrap();
/// ```
Expand Down
12 changes: 7 additions & 5 deletions tracing-subscriber/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,18 @@
//!
//! ```rust
//! use tracing_subscriber::{fmt, Layer, registry::Registry, EnvFilter};
//! use tracing_subscriber::prelude::*;
//!
//! let fmt_layer = fmt::Layer::builder()
//! .with_target(false)
//! .finish();
//!
//! let subscriber = EnvFilter::try_from_default_env()
//! let filter_layer = EnvFilter::try_from_default_env()
//! .or_else(|_| EnvFilter::try_new("info"))
//! .unwrap()
//! .and_then(fmt_layer)
//! .with_subscriber(Registry::default());
//! .unwrap();
//!
//! let subscriber = Registry::default()
//! .with(filter_layer)
//! .with(fmt_layer);
//!
//! tracing::subscriber::set_global_default(subscriber).unwrap();
//! ```
Expand Down
131 changes: 126 additions & 5 deletions tracing-subscriber/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,131 @@ use std::{any::TypeId, marker::PhantomData};
/// [`Subscriber`] behavior; it can _observe_ events and spans, but does not
/// assign IDs.
///
/// ## Composing Layers
///
/// Since a `Layer` does not implement a complete strategy for collecting
/// traces, it must be composed with a `Subscriber` in order to be used. The
/// `Layer` trait is generic over a type parameter (called `S` in the trait
/// definition), representing the types of `Subscriber` they can be composed
/// with. Thus, a `Layer` may be implemented that will only compose with a
/// particular `Subscriber` implementation, or additional trait bounds may be
/// added to constrain what types implementing `Subscriber` a `Layer` can wrap.
///
/// `Layer`s may be added to a `Subscriber` by using the [`SubscriberExt::with`]
/// method, which is provided by `tracing-subscriber`'s [prelude]. This method
/// returns a [`Layered`] struct that implements `Subscriber` by composing the
/// `Layer` with the `Subscriber`.
///
/// For example:
/// ```rust
/// use tracing_subscriber::Layer;
/// use tracing_subscriber::prelude::*;
/// use tracing::Subscriber;
///
/// pub struct MyLayer {
/// // ...
/// }
///
/// impl<S: Subscriber> Layer<S> for MyLayer {
/// // ...
/// }
///
/// pub struct MySubscriber {
/// // ...
/// }
///
/// # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event};
/// impl Subscriber for MySubscriber {
/// // ...
/// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) }
/// # fn record(&self, _: &Id, _: &Record) {}
/// # fn event(&self, _: &Event) {}
/// # fn record_follows_from(&self, _: &Id, _: &Id) {}
/// # fn enabled(&self, _: &Metadata) -> bool { false }
/// # fn enter(&self, _: &Id) {}
/// # fn exit(&self, _: &Id) {}
/// }
/// # impl MyLayer {
/// # fn new() -> Self { Self {} }
/// # }
/// # impl MySubscriber {
/// # fn new() -> Self { Self { }}
/// # }
///
/// let subscriber = MySubscriber::new()
/// .with(MyLayer::new());
///
/// tracing::subscriber::set_global_default(subscriber);
/// ```
///
/// Multiple `Layer`s may be composed in the same manner:
/// ```rust
/// # use tracing_subscriber::Layer;
/// # use tracing_subscriber::prelude::*;
/// # use tracing::Subscriber;
/// pub struct MyOtherLayer {
/// // ...
/// }
///
/// impl<S: Subscriber> Layer<S> for MyOtherLayer {
/// // ...
/// }
///
/// pub struct MyThirdLayer {
/// // ...
/// }
///
/// impl<S: Subscriber> Layer<S> for MyThirdLayer {
/// // ...
/// }
/// # pub struct MyLayer {}
/// # impl<S: Subscriber> Layer<S> for MyLayer {}
/// # pub struct MySubscriber { }
/// # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event};
/// # impl Subscriber for MySubscriber {
/// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) }
/// # fn record(&self, _: &Id, _: &Record) {}
/// # fn event(&self, _: &Event) {}
/// # fn record_follows_from(&self, _: &Id, _: &Id) {}
/// # fn enabled(&self, _: &Metadata) -> bool { false }
/// # fn enter(&self, _: &Id) {}
/// # fn exit(&self, _: &Id) {}
/// }
/// # impl MyLayer {
/// # fn new() -> Self { Self {} }
/// # }
/// # impl MyOtherLayer {
/// # fn new() -> Self { Self {} }
/// # }
/// # impl MyThirdLayer {
/// # fn new() -> Self { Self {} }
/// # }
/// # impl MySubscriber {
/// # fn new() -> Self { Self { }}
/// # }
///
/// let subscriber = MySubscriber::new()
/// .with(MyLayer::new())
/// .with(MyOtherLayer::new())
/// .with(MyThirdLayer::new());
///
/// tracing::subscriber::set_global_default(subscriber);
/// ```
///
/// The [`Layer::with_subscriber` method][with-sub] constructs the `Layered`
/// type from a `Layer` and `Subscriber`, and is called by
/// [`SubscriberExt::with`]. In general, it is more idiomatic to use
/// `SubscriberExt::with`, and treat `Layer::with_subscriber` as an
/// implementation detail, as `with_subscriber` calls must be nested, leading to
/// less clear code for the reader. However, `Layer`s which wish to perform
/// additional behavior when composed with a subscriber may provide their own
/// implementations of `SubscriberExt::with`.
///
/// [`SubscriberExt::with`]: trait.SubscriberExt.html#method.with
/// [`Layered`]: struct.Layered.html
/// [prelude]: ../prelude/index.html
/// [with-sub]: #method.with_subscriber
///
/// ## Recording Traces
///
/// The `Layer` trait defines a set of methods for consuming notifications from
Expand Down Expand Up @@ -371,11 +496,7 @@ pub trait SubscriberExt: Subscriber + crate::sealed::Sealed {
L: Layer<Self>,
Self: Sized,
{
Layered {
layer,
inner: self,
_s: PhantomData,
}
layer.with_subscriber(self)
}
}

Expand Down
10 changes: 5 additions & 5 deletions tracing-subscriber/src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//!
//! For example, we might create a `Registry` and add multiple `Layer`s like so:
//! ```rust
//! use tracing_subscriber::{registry::Registry, Layer};
//! use tracing_subscriber::{registry::Registry, Layer, prelude::*};
//! # use tracing_core::Subscriber;
//! # pub struct FooLayer {}
//! # pub struct BarLayer {}
Expand All @@ -23,12 +23,12 @@
//! # fn new() -> Self { Self {} }
//! # }
//! # impl BarLayer {
//! # fn new() -> Self { Self { }}
//! # fn new() -> Self { Self {} }
//! # }
//!
//! let subscriber = FooLayer::new()
//! .and_then(BarLayer::new())
//! .with_subscriber(Registry::default());
//! let subscriber = Registry::default()
//! .with(FooLayer::new())
//! .with(BarLayer::new());
//! ```
//!
//! If a type implementing `Layer` depends on the functionality of a `Registry`
Expand Down

0 comments on commit e32012a

Please sign in to comment.