diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 3cd55c832f..3b1cb3bda0 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -264,7 +264,9 @@ //! .init(); //! ``` //! -//! [prelude]: crate::prelude +//! The [`Layer::boxed`] method is provided to make boxing a `Layer` +//! more convenient, but [`Box::new`] may be used as well. +//! //! [option-impl]: Layer#impl-Layer-for-Option //! [box-impl]: Layer#impl-Layer%3CS%3E-for-Box%3Cdyn%20Layer%3CS%3E%20+%20Send%20+%20Sync%3E //! [prelude]: crate::prelude @@ -921,6 +923,131 @@ where filter::Filtered::new(self, filter) } + /// Erases the type of this [`Layer`], returning a [`Box`]ed `dyn + /// Layer` trait object. + /// + /// This can be used when a function returns a `Layer` which may be of + /// one of several types, or when a `Layer` subscriber has a very long type + /// signature. + /// + /// # Examples + /// + /// The following example will *not* compile, because the value assigned to + /// `log_layer` may have one of several different types: + /// + /// ```compile_fail + /// # fn main() -> Result<(), Box> { + /// use tracing_subscriber::{Layer, 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 layer of one of several types. + /// let log_layer = match config { + /// // If logging to a file, use a maximally-verbose configuration. + /// LogConfig::File(path) => { + /// let file = File::create(path)?; + /// tracing_subscriber::fmt::layer() + /// .with_thread_ids(true) + /// .with_thread_names(true) + /// // Selecting the JSON logging format changes the layer's + /// // type. + /// .json() + /// .with_span_list(true) + /// // Setting the writer to use our log file changes the + /// // layer's type again. + /// .with_writer(file) + /// }, + /// + /// // If logging to stdout, use a pretty, human-readable configuration. + /// LogConfig::Stdout => tracing_subscriber::fmt::layer() + /// // Selecting the "pretty" logging format changes the + /// // layer'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::layer() + /// // Changing the writer changes the layer's type + /// .with_writer(io::stderr) + /// // Only log the `WARN` and `ERROR` levels. Adding a filter + /// // changes the layer's type to `Filtered`. + /// .with_filter(LevelFilter::WARN), + /// }; + /// + /// tracing_subscriber::registry() + /// .with(log_layer) + /// .init(); + /// # Ok(()) } + /// ``` + /// + /// However, adding a call to `.boxed()` after each match arm erases the + /// layer's type, so this code *does* compile: + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*}; + /// # use std::{path::PathBuf, fs::File, io}; + /// # pub enum LogConfig { + /// # File(PathBuf), + /// # Stdout, + /// # Stderr, + /// # } + /// # let config = LogConfig::Stdout; + /// let log_layer = match config { + /// LogConfig::File(path) => { + /// let file = File::create(path)?; + /// tracing_subscriber::fmt::layer() + /// .with_thread_ids(true) + /// .with_thread_names(true) + /// .json() + /// .with_span_list(true) + /// .with_writer(file) + /// // Erase the type by boxing the layer + /// .boxed() + /// }, + /// + /// LogConfig::Stdout => tracing_subscriber::fmt::layer() + /// .pretty() + /// .with_writer(io::stdout) + /// .and_then(tracing_subscriber::EnvFilter::from_default_env()) + /// // Erase the type by boxing the layer + /// .boxed(), + /// + /// LogConfig::Stderr => tracing_subscriber::fmt::layer() + /// .with_writer(io::stderr) + /// .with_filter(LevelFilter::WARN) + /// // Erase the type by boxing the layer + /// .boxed(), + /// }; + /// + /// tracing_subscriber::registry() + /// .with(log_layer) + /// .init(); + /// # Ok(()) } + /// ``` + #[cfg(any(feature = "alloc", feature = "std"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + fn boxed(self) -> Box + Send + Sync + 'static> + where + Self: Sized, + Self: Layer + Send + Sync + 'static, + S: Subscriber, + { + Box::new(self) + } + #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { if id == TypeId::of::() {