Skip to content

Commit 6380c71

Browse files
committed
subscriber: add Layer::boxed method (#2026)
## Motivation PR #2023 made the `impl Layer` for boxed `dyn Layer` 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 `Layer`'s type, it's necessary to add a type annotation like this: ```rust let boxed_layer: Box<dyn Layer<_> + Send + Sync + 'static> = Box::new(some_layer); ``` Because the trait object includes `Send + Sync + 'static`, this is a bit wordy to type out. ## Solution This commit adds a `Layer::boxed` method that takes `Self` and boxes it as a type-erased `dyn Layer` trait object. This can then be chained after a a chain of layer builder methods. Because the `boxed` method _explicitly_ returns a `dyn Layer<_> + ...` trait object, it's not necessary to use a wordy type annotation when using this method.
1 parent 871f12a commit 6380c71

File tree

1 file changed

+128
-1
lines changed
  • tracing-subscriber/src/layer

1 file changed

+128
-1
lines changed

tracing-subscriber/src/layer/mod.rs

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,9 @@
264264
//! .init();
265265
//! ```
266266
//!
267-
//! [prelude]: crate::prelude
267+
//! The [`Layer::boxed`] method is provided to make boxing a `Layer`
268+
//! more convenient, but [`Box::new`] may be used as well.
269+
//!
268270
//! [option-impl]: Layer#impl-Layer<S>-for-Option<L>
269271
//! [box-impl]: Layer#impl-Layer%3CS%3E-for-Box%3Cdyn%20Layer%3CS%3E%20+%20Send%20+%20Sync%3E
270272
//! [prelude]: crate::prelude
@@ -921,6 +923,131 @@ where
921923
filter::Filtered::new(self, filter)
922924
}
923925

926+
/// Erases the type of this [`Layer`], returning a [`Box`]ed `dyn
927+
/// Layer` trait object.
928+
///
929+
/// This can be used when a function returns a `Layer` which may be of
930+
/// one of several types, or when a `Layer` subscriber has a very long type
931+
/// signature.
932+
///
933+
/// # Examples
934+
///
935+
/// The following example will *not* compile, because the value assigned to
936+
/// `log_layer` may have one of several different types:
937+
///
938+
/// ```compile_fail
939+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
940+
/// use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*};
941+
/// use std::{path::PathBuf, fs::File, io};
942+
///
943+
/// /// Configures whether logs are emitted to a file, to stdout, or to stderr.
944+
/// pub enum LogConfig {
945+
/// File(PathBuf),
946+
/// Stdout,
947+
/// Stderr,
948+
/// }
949+
///
950+
/// let config = // ...
951+
/// # LogConfig::Stdout;
952+
///
953+
/// // Depending on the config, construct a layer of one of several types.
954+
/// let log_layer = match config {
955+
/// // If logging to a file, use a maximally-verbose configuration.
956+
/// LogConfig::File(path) => {
957+
/// let file = File::create(path)?;
958+
/// tracing_subscriber::fmt::layer()
959+
/// .with_thread_ids(true)
960+
/// .with_thread_names(true)
961+
/// // Selecting the JSON logging format changes the layer's
962+
/// // type.
963+
/// .json()
964+
/// .with_span_list(true)
965+
/// // Setting the writer to use our log file changes the
966+
/// // layer's type again.
967+
/// .with_writer(file)
968+
/// },
969+
///
970+
/// // If logging to stdout, use a pretty, human-readable configuration.
971+
/// LogConfig::Stdout => tracing_subscriber::fmt::layer()
972+
/// // Selecting the "pretty" logging format changes the
973+
/// // layer's type!
974+
/// .pretty()
975+
/// .with_writer(io::stdout)
976+
/// // Add a filter based on the RUST_LOG environment variable;
977+
/// // this changes the type too!
978+
/// .and_then(tracing_subscriber::EnvFilter::from_default_env()),
979+
///
980+
/// // If logging to stdout, only log errors and warnings.
981+
/// LogConfig::Stderr => tracing_subscriber::fmt::layer()
982+
/// // Changing the writer changes the layer's type
983+
/// .with_writer(io::stderr)
984+
/// // Only log the `WARN` and `ERROR` levels. Adding a filter
985+
/// // changes the layer's type to `Filtered<LevelFilter, ...>`.
986+
/// .with_filter(LevelFilter::WARN),
987+
/// };
988+
///
989+
/// tracing_subscriber::registry()
990+
/// .with(log_layer)
991+
/// .init();
992+
/// # Ok(()) }
993+
/// ```
994+
///
995+
/// However, adding a call to `.boxed()` after each match arm erases the
996+
/// layer's type, so this code *does* compile:
997+
///
998+
/// ```
999+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1000+
/// # use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*};
1001+
/// # use std::{path::PathBuf, fs::File, io};
1002+
/// # pub enum LogConfig {
1003+
/// # File(PathBuf),
1004+
/// # Stdout,
1005+
/// # Stderr,
1006+
/// # }
1007+
/// # let config = LogConfig::Stdout;
1008+
/// let log_layer = match config {
1009+
/// LogConfig::File(path) => {
1010+
/// let file = File::create(path)?;
1011+
/// tracing_subscriber::fmt::layer()
1012+
/// .with_thread_ids(true)
1013+
/// .with_thread_names(true)
1014+
/// .json()
1015+
/// .with_span_list(true)
1016+
/// .with_writer(file)
1017+
/// // Erase the type by boxing the layer
1018+
/// .boxed()
1019+
/// },
1020+
///
1021+
/// LogConfig::Stdout => tracing_subscriber::fmt::layer()
1022+
/// .pretty()
1023+
/// .with_writer(io::stdout)
1024+
/// .and_then(tracing_subscriber::EnvFilter::from_default_env())
1025+
/// // Erase the type by boxing the layer
1026+
/// .boxed(),
1027+
///
1028+
/// LogConfig::Stderr => tracing_subscriber::fmt::layer()
1029+
/// .with_writer(io::stderr)
1030+
/// .with_filter(LevelFilter::WARN)
1031+
/// // Erase the type by boxing the layer
1032+
/// .boxed(),
1033+
/// };
1034+
///
1035+
/// tracing_subscriber::registry()
1036+
/// .with(log_layer)
1037+
/// .init();
1038+
/// # Ok(()) }
1039+
/// ```
1040+
#[cfg(any(feature = "alloc", feature = "std"))]
1041+
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
1042+
fn boxed(self) -> Box<dyn Layer<S> + Send + Sync + 'static>
1043+
where
1044+
Self: Sized,
1045+
Self: Layer<S> + Send + Sync + 'static,
1046+
S: Subscriber,
1047+
{
1048+
Box::new(self)
1049+
}
1050+
9241051
#[doc(hidden)]
9251052
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
9261053
if id == TypeId::of::<Self>() {

0 commit comments

Comments
 (0)