Skip to content

Commit

Permalink
subscriber: add Subscribe::boxed method
Browse files Browse the repository at this point in the history
## Motivation

PR #2023 made the `impl Subscribe` for boxed `dyn Subscribe` trait
objects actually be useful. One minor annoyance when working with boxed
trait objects is that, in some cases, in order to use `Box::new` to
erase a subscriber's type, it's necessary to add a type annotation like
this:
```rust
let boxed_subscriber: Box<dyn Subscribe<_> + Send + Sync + 'static>
    = Box::new(some_subscriber);
```

Because the trait object includes `Send + Sync + 'static`, this is a bit
wordy to type out.

## Solution

This commit adds a `Subscribe::boxed` method that takes `Self` and boxes
it as a type-erased `dyn Subscribe` trait object. This can then be
chained after a a chain of subscriber builder methods. Because the
`boxed` method _explicitly_ returns a `dyn Subscribe + ...` trait
object, it's not necessary to use a wordy type annotation when
using this method.
  • Loading branch information
hawkw committed Mar 25, 2022
1 parent c1de498 commit b0f62e0
Showing 1 changed file with 129 additions and 1 deletion.
130 changes: 129 additions & 1 deletion tracing-subscriber/src/subscribe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@
//! .init();
//! ```
//!
//! The [`Subscribe::boxed`] method is provided to make boxing a subscriber
//! more convenient, but [`Box::new`] may be used as well.
//!
//! [prelude]: crate::prelude
//! [option-impl]: crate::subscribe::Subscribe#impl-Subscribe<C>-for-Option<S>
//! [box-impl]: Subscribe#impl-Subscribe%3CC%3E-for-Box%3Cdyn%20Subscribe%3CC%3E%20+%20Send%20+%20Sync%20+%20%27static%3E
Expand Down Expand Up @@ -371,7 +374,7 @@
//! metadata.target() == "http_access"
//! }));
//!
//! // A gteneral-purpose logging subscriber.
//! // A general-purpose logging subscriber.
//! let fmt_subscriber = tracing_subscriber::fmt::subscriber();
//!
//! // Build a subscriber that combines the access log and stdout log
Expand Down Expand Up @@ -919,6 +922,131 @@ where
filter::Filtered::new(self, filter)
}

/// Erases the type of this subscriber, returning a [`Box`]ed `dyn
/// Subscribe` trait object.
///
/// This can be used when a function returns a subscriber which may be of
/// one of several types, or when a composed subscriber has a very long type
/// signature.
///
/// # Examples
///
/// The following example will *not* compile, because the value assigned to
/// `log_subscriber` may have one of several different types:
///
/// ```compile_fail
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tracing_subscriber::{Subscribe, filter::LevelFilter, prelude::*};
/// use std::{path::PathBuf, fs::File, io};
///
/// /// Configures whether logs are emitted to a file, to stdout, or to stderr.
/// pub enum LogConfig {
/// File(PathBuf),
/// Stdout,
/// Stderr,
/// }
///
/// let config = // ...
/// # LogConfig::Stdout;
///
/// // Depending on the config, construct a subscriber of one of several types.
/// let log_subscriber = match config {
/// // If logging to a file, use a maximally-verbose configuration.
/// LogConfig::File(path) => {
/// let file = File::create(path)?;
/// tracing_subscriber::fmt::subscriber()
/// .with_thread_ids(true)
/// .with_thread_names(true)
/// // Selecting the JSON logging format changes the subscriber's
/// // type.
/// .json()
/// .with_span_list(true)
/// // Setting the writer to use our log file changes the
/// // subscriber's type again.
/// .with_writer(file)
/// },
///
/// // If logging to stdout, use a pretty, human-readable configuration.
/// LogConfig::Stdout => tracing_subscriber::fmt::subscriber()
/// // Selecting the "pretty" logging format changes the
/// // subscriber's type!
/// .pretty()
/// .with_writer(io::stdout)
/// // Add a filter based on the RUST_LOG environment variable;
/// // this changes the type too!
/// .and_then(tracing_subscriber::EnvFilter::from_default_env()),
///
/// // If logging to stdout, only log errors and warnings.
/// LogConfig::Stderr => tracing_subscriber::fmt::subscriber()
/// // Changing the writer changes the subscriber's type
/// .with_writer(io::stderr)
/// // Only log the `WARN` and `ERROR` levels. Adding a filter
/// // changes the subscriber's type to `Filtered<LevelFilter, ...>`.
/// .with_filter(LevelFilter::WARN),
/// };
///
/// tracing_subscriber::registry()
/// .with(log_subscriber)
/// .init();
/// # Ok(()) }
/// ```
///
/// However, adding a call to `.boxed()` after each match arm erases the
/// subscriber's type, so this code *does* compile:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # use tracing_subscriber::{Subscribe, filter::LevelFilter, prelude::*};
/// # use std::{path::PathBuf, fs::File, io};
/// # pub enum LogConfig {
/// # File(PathBuf),
/// # Stdout,
/// # Stderr,
/// # }
/// # let config = LogConfig::Stdout;
/// let log_subscriber = match config {
/// LogConfig::File(path) => {
/// let file = File::create(path)?;
/// tracing_subscriber::fmt::subscriber()
/// .with_thread_ids(true)
/// .with_thread_names(true)
/// .json()
/// .with_span_list(true)
/// .with_writer(file)
/// // Erase the type by boxing the subscriber
/// .boxed()
/// },
///
/// LogConfig::Stdout => tracing_subscriber::fmt::subscriber()
/// .pretty()
/// .with_writer(io::stdout)
/// .and_then(tracing_subscriber::EnvFilter::from_default_env())
/// // Erase the type by boxing the subscriber
/// .boxed(),
///
/// LogConfig::Stderr => tracing_subscriber::fmt::subscriber()
/// .with_writer(io::stderr)
/// .with_filter(LevelFilter::WARN)
/// // Erase the type by boxing the subscriber
/// .boxed(),
/// };
///
/// tracing_subscriber::registry()
/// .with(log_subscriber)
/// .init();
/// # Ok(()) }
/// ```
#[cfg(any(feature = "alloc", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
fn boxed(self) -> Box<dyn Subscribe<C> + Send + Sync + 'static>
where
Self: Sized,
Self: Subscribe<C> + Send + Sync + 'static,
C: Collect,
{
Box::new(self)
}

#[doc(hidden)]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<NonNull<()>> {
if id == TypeId::of::<Self>() {
Expand Down

0 comments on commit b0f62e0

Please sign in to comment.