tracing_subscriber/
util.rs

1//! Extension traits and other utilities to make working with subscribers more
2//! ergonomic.
3
4#[cfg(feature = "std")]
5use alloc::boxed::Box;
6use core::fmt;
7#[cfg(feature = "std")]
8use std::error::Error;
9use tracing_core::dispatcher::{self, Dispatch};
10#[cfg(feature = "tracing-log")]
11use tracing_log::AsLog;
12
13/// Extension trait adding utility methods for subscriber initialization.
14///
15/// This trait provides extension methods to make configuring and setting a
16/// [default subscriber] more ergonomic. It is automatically implemented for all
17/// types that can be converted into a [trace dispatcher]. Since `Dispatch`
18/// implements `From<T>` for all `T: Subscriber`, all `Subscriber`
19/// implementations will implement this extension trait as well. Types which
20/// can be converted into `Subscriber`s, such as builders that construct a
21/// `Subscriber`, may implement `Into<Dispatch>`, and will also receive an
22/// implementation of this trait.
23///
24/// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
25/// [trace dispatcher]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html
26pub trait SubscriberInitExt
27where
28    Self: Into<Dispatch>,
29{
30    /// Sets `self` as the [default subscriber] in the current scope, returning a
31    /// guard that will unset it when dropped.
32    ///
33    /// If the "tracing-log" feature flag is enabled, this will also initialize
34    /// a [`log`] compatibility layer. This allows the subscriber to consume
35    /// `log::Record`s as though they were `tracing` `Event`s.
36    ///
37    /// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
38    /// [`log`]: https://crates.io/log
39    #[cfg(feature = "std")]
40    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
41    fn set_default(self) -> dispatcher::DefaultGuard {
42        #[cfg(feature = "tracing-log")]
43        let _ = tracing_log::LogTracer::init();
44
45        dispatcher::set_default(&self.into())
46    }
47
48    /// Attempts to set `self` as the [global default subscriber] in the current
49    /// scope, returning an error if one is already set.
50    ///
51    /// If the "tracing-log" feature flag is enabled, this will also attempt to
52    /// initialize a [`log`] compatibility layer. This allows the subscriber to
53    /// consume `log::Record`s as though they were `tracing` `Event`s.
54    ///
55    /// This method returns an error if a global default subscriber has already
56    /// been set, or if a `log` logger has already been set (when the
57    /// "tracing-log" feature is enabled).
58    ///
59    /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
60    /// [`log`]: https://crates.io/log
61    fn try_init(self) -> Result<(), TryInitError> {
62        dispatcher::set_global_default(self.into()).map_err(TryInitError::new)?;
63
64        // Since we are setting the global default subscriber, we can
65        // opportunistically go ahead and set its global max level hint as
66        // the max level for the `log` crate as well. This should make
67        // skipping `log` diagnostics much faster.
68        #[cfg(feature = "tracing-log")]
69        tracing_log::LogTracer::builder()
70            // Note that we must call this *after* setting the global default
71            // subscriber, so that we get its max level hint.
72            .with_max_level(tracing_core::LevelFilter::current().as_log())
73            .init()
74            .map_err(TryInitError::new)?;
75
76        Ok(())
77    }
78
79    /// Attempts to set `self` as the [global default subscriber] in the current
80    /// scope, panicking if this fails.
81    ///
82    /// If the "tracing-log" feature flag is enabled, this will also attempt to
83    /// initialize a [`log`] compatibility layer. This allows the subscriber to
84    /// consume `log::Record`s as though they were `tracing` `Event`s.
85    ///
86    /// This method panics if a global default subscriber has already been set,
87    /// or if a `log` logger has already been set (when the "tracing-log"
88    /// feature is enabled).
89    ///
90    /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber
91    /// [`log`]: https://crates.io/log
92    fn init(self) {
93        self.try_init()
94            .expect("failed to set global default subscriber")
95    }
96}
97
98impl<T> SubscriberInitExt for T where T: Into<Dispatch> {}
99
100/// Error returned by [`try_init`](SubscriberInitExt::try_init) if a global default subscriber could not be initialized.
101pub struct TryInitError {
102    #[cfg(feature = "std")]
103    inner: Box<dyn Error + Send + Sync + 'static>,
104
105    #[cfg(not(feature = "std"))]
106    _p: (),
107}
108
109// ==== impl TryInitError ====
110
111impl TryInitError {
112    #[cfg(feature = "std")]
113    fn new(e: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
114        Self { inner: e.into() }
115    }
116
117    #[cfg(not(feature = "std"))]
118    fn new<T>(_: T) -> Self {
119        Self { _p: () }
120    }
121}
122
123impl fmt::Debug for TryInitError {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        #[cfg(feature = "std")]
126        {
127            fmt::Debug::fmt(&self.inner, f)
128        }
129
130        #[cfg(not(feature = "std"))]
131        {
132            f.write_str("TryInitError(())")
133        }
134    }
135}
136
137impl fmt::Display for TryInitError {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        #[cfg(feature = "std")]
140        {
141            fmt::Display::fmt(&self.inner, f)
142        }
143
144        #[cfg(not(feature = "std"))]
145        {
146            f.write_str("failed to set global default subscriber")
147        }
148    }
149}
150
151#[cfg(feature = "std")]
152impl Error for TryInitError {
153    fn source(&self) -> Option<&(dyn Error + 'static)> {
154        self.inner.source()
155    }
156}