Skip to content

Commit

Permalink
subscriber: Add SubscriberBuilder/Layer accessors (tokio-rs#1871)
Browse files Browse the repository at this point in the history
## Motivation

`SubscriberBuilder`s and `Layer`s configured with custom event/field
formatters do not provide any means of accessing or mutating those
formatters. Any configuration that needs to be done must be done before
setting them on the builder/layer. This is frustrating as it makes it
difficult to provide a pre-configured API akin to
`tracing_subscriber::fmt()` along with accessors like `.compact()` that
modify the formatter.

## Solution

Add accessors `.map_event_format()` and `.map_fmt_fields()` to
`SubscriberBuilder` and `Layer` that map the existing formatter through
a closure. This allows the closure to modify it or to derive a new
formatter from it with a different type.

Also add a `.map_writer()` method that does the same thing for the
`MakeWriter`, to round out the accessors for the various type
parameters.

The filter type is currently restricted to just `LevelFilter` or
`EnvFilter` and so this does not add a corresponding `.map_filter()`.
That can be added later if we add the ability to attach arbitrary
filters.

Also fix some minor docs issues that were spotted as part of
implementing this.

Fixes tokio-rs#1756
  • Loading branch information
lilyball authored Jan 28, 2022
1 parent 24ee184 commit bcd0972
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 15 deletions.
101 changes: 95 additions & 6 deletions tracing-subscriber/src/fmt/fmt_layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ where
N: for<'writer> FormatFields<'writer> + 'static,
W: for<'writer> MakeWriter<'writer> + 'static,
{
/// Sets the [event formatter][`FormatEvent`] that the layer will use to
/// format events.
/// Sets the [event formatter][`FormatEvent`] that the layer being built will
/// use to format events.
///
/// The event formatter may be any type implementing the [`FormatEvent`]
/// trait, which is implemented for all functions taking a [`FmtContext`], a
Expand Down Expand Up @@ -122,11 +122,40 @@ where
_inner: self._inner,
}
}

/// Updates the event formatter by applying a function to the existing event formatter.
///
/// This sets the event formatter that the layer being built will use to record fields.
///
/// # Examples
///
/// Updating an event formatter:
///
/// ```rust
/// let layer = tracing_subscriber::fmt::layer()
/// .map_event_format(|e| e.compact());
/// # // this is necessary for type inference.
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
pub fn map_event_format<E2>(self, f: impl FnOnce(E) -> E2) -> Layer<S, N, E2, W>
where
E2: FormatEvent<S, N> + 'static,
{
Layer {
fmt_fields: self.fmt_fields,
fmt_event: f(self.fmt_event),
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
_inner: self._inner,
}
}
}

// This needs to be a seperate impl block because they place different bounds on the type parameters.
impl<S, N, E, W> Layer<S, N, E, W> {
/// Sets the [`MakeWriter`] that the [`Layer`] being built will use to write events.
/// Sets the [`MakeWriter`] that the layer being built will use to write events.
///
/// # Examples
///
Expand All @@ -142,9 +171,6 @@ impl<S, N, E, W> Layer<S, N, E, W> {
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
///
/// [`MakeWriter`]: ../fmt/trait.MakeWriter.html
/// [`Layer`]: ../layer/trait.Layer.html
pub fn with_writer<W2>(self, make_writer: W2) -> Layer<S, N, E, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
Expand Down Expand Up @@ -201,6 +227,39 @@ impl<S, N, E, W> Layer<S, N, E, W> {
..self
}
}

/// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
///
/// This sets the [`MakeWriter`] that the layer being built will use to write events.
///
/// # Examples
///
/// Redirect output to stderr if level is <= WARN:
///
/// ```rust
/// use tracing::Level;
/// use tracing_subscriber::fmt::{self, writer::MakeWriterExt};
///
/// let stderr = std::io::stderr.with_max_level(Level::WARN);
/// let layer = fmt::layer()
/// .map_writer(move |w| stderr.or_else(w));
/// # // this is necessary for type inference.
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> Layer<S, N, E, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
{
Layer {
fmt_fields: self.fmt_fields,
fmt_event: self.fmt_event,
fmt_span: self.fmt_span,
is_ansi: self.is_ansi,
make_writer: f(self.make_writer),
_inner: self._inner,
}
}
}

impl<S, N, L, T, W> Layer<S, N, format::Format<L, T>, W>
Expand Down Expand Up @@ -481,6 +540,36 @@ impl<S, N, E, W> Layer<S, N, E, W> {
_inner: self._inner,
}
}

/// Updates the field formatter by applying a function to the existing field formatter.
///
/// This sets the field formatter that the layer being built will use to record fields.
///
/// # Examples
///
/// Updating a field formatter:
///
/// ```rust
/// use tracing_subscriber::field::MakeExt;
/// let layer = tracing_subscriber::fmt::layer()
/// .map_fmt_fields(|f| f.debug_alt());
/// # // this is necessary for type inference.
/// # use tracing_subscriber::Layer as _;
/// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default());
/// ```
pub fn map_fmt_fields<N2>(self, f: impl FnOnce(N) -> N2) -> Layer<S, N2, E, W>
where
N2: for<'writer> FormatFields<'writer> + 'static,
{
Layer {
fmt_event: self.fmt_event,
fmt_fields: f(self.fmt_fields),
fmt_span: self.fmt_span,
make_writer: self.make_writer,
is_ansi: self.is_ansi,
_inner: self._inner,
}
}
}

impl<S> Default for Layer<S> {
Expand Down
108 changes: 100 additions & 8 deletions tracing-subscriber/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ where
}

impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// Sets the Visitor that the subscriber being built will use to record
/// Sets the field formatter that the subscriber being built will use to record
/// fields.
///
/// For example:
Expand Down Expand Up @@ -1021,7 +1021,7 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// subscriber.
///
/// If the max level has already been set, or a [`EnvFilter`] was added by
/// [`with_filter`], this replaces that configuration with the new
/// [`with_env_filter`], this replaces that configuration with the new
/// maximum level.
///
/// # Examples
Expand All @@ -1044,8 +1044,8 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// .finish();
/// ```
/// [verbosity level]: https://docs.rs/tracing-core/0.1.5/tracing_core/struct.Level.html
/// [`EnvFilter`]: ../filter/struct.EnvFilter.html
/// [`with_filter`]: #method.with_filter
/// [`EnvFilter`]: struct@crate::filter::EnvFilter
/// [`with_env_filter`]: fn@Self::with_env_filter
pub fn with_max_level(
self,
filter: impl Into<LevelFilter>,
Expand All @@ -1057,8 +1057,26 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
}
}

/// Sets the function that the subscriber being built should use to format
/// events that occur.
/// Sets the [event formatter][`FormatEvent`] that the subscriber being built
/// will use to format events that occur.
///
/// The event formatter may be any type implementing the [`FormatEvent`]
/// trait, which is implemented for all functions taking a [`FmtContext`], a
/// [`Writer`], and an [`Event`].
///
/// # Examples
///
/// Setting a type implementing [`FormatEvent`] as the formatter:
///
/// ```rust
/// use tracing_subscriber::fmt::format;
///
/// let subscriber = tracing_subscriber::fmt()
/// .event_format(format().compact())
/// .finish();
/// ```
///
/// [`Writer`]: struct@self::format::Writer
pub fn event_format<E2>(self, fmt_event: E2) -> SubscriberBuilder<N, E2, F, W>
where
E2: FormatEvent<Registry, N> + 'static,
Expand All @@ -1085,8 +1103,6 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
/// .with_writer(io::stderr)
/// .init();
/// ```
///
/// [`MakeWriter`]: trait.MakeWriter.html
pub fn with_writer<W2>(self, make_writer: W2) -> SubscriberBuilder<N, E, F, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
Expand Down Expand Up @@ -1127,6 +1143,82 @@ impl<N, E, F, W> SubscriberBuilder<N, E, F, W> {
inner: self.inner.with_writer(TestWriter::default()),
}
}

/// Updates the event formatter by applying a function to the existing event formatter.
///
/// This sets the event formatter that the subscriber being built will use to record fields.
///
/// # Examples
///
/// Updating an event formatter:
///
/// ```rust
/// let subscriber = tracing_subscriber::fmt()
/// .map_event_format(|e| e.compact())
/// .finish();
/// ```
pub fn map_event_format<E2>(self, f: impl FnOnce(E) -> E2) -> SubscriberBuilder<N, E2, F, W>
where
E2: FormatEvent<Registry, N> + 'static,
N: for<'writer> FormatFields<'writer> + 'static,
W: for<'writer> MakeWriter<'writer> + 'static,
{
SubscriberBuilder {
filter: self.filter,
inner: self.inner.map_event_format(f),
}
}

/// Updates the field formatter by applying a function to the existing field formatter.
///
/// This sets the field formatter that the subscriber being built will use to record fields.
///
/// # Examples
///
/// Updating a field formatter:
///
/// ```rust
/// use tracing_subscriber::field::MakeExt;
/// let subscriber = tracing_subscriber::fmt()
/// .map_fmt_fields(|f| f.debug_alt())
/// .finish();
/// ```
pub fn map_fmt_fields<N2>(self, f: impl FnOnce(N) -> N2) -> SubscriberBuilder<N2, E, F, W>
where
N2: for<'writer> FormatFields<'writer> + 'static,
{
SubscriberBuilder {
filter: self.filter,
inner: self.inner.map_fmt_fields(f),
}
}

/// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
///
/// This sets the [`MakeWriter`] that the subscriber being built will use to write events.
///
/// # Examples
///
/// Redirect output to stderr if level is <= WARN:
///
/// ```rust
/// use tracing::Level;
/// use tracing_subscriber::fmt::{self, writer::MakeWriterExt};
///
/// let stderr = std::io::stderr.with_max_level(Level::WARN);
/// let layer = tracing_subscriber::fmt()
/// .map_writer(move |w| stderr.or_else(w))
/// .finish();
/// ```
pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> SubscriberBuilder<N, E, F, W2>
where
W2: for<'writer> MakeWriter<'writer> + 'static,
{
SubscriberBuilder {
filter: self.filter,
inner: self.inner.map_writer(f),
}
}
}

/// Install a global tracing subscriber that listens for events and
Expand Down
2 changes: 1 addition & 1 deletion tracing-subscriber/src/layer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ where
/// per-layer filtering.
///
/// [`Filtered`]: crate::filter::Filtered
/// [plf]: #per-layer-filtering
/// [plf]: crate::layer#per-layer-filtering
#[cfg(all(feature = "registry", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
fn with_filter<F>(self, filter: F) -> filter::Filtered<Self, F, S>
Expand Down

0 comments on commit bcd0972

Please sign in to comment.