tracing_subscriber/
util.rs

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