1 //! Extension traits and other utilities to make working with subscribers more
2 //! ergonomic.
3 use core::fmt;
4 #[cfg(feature = "std")]
5 use std::error::Error;
6 use tracing_core::dispatcher::{self, Dispatch};
7 #[cfg(feature = "tracing-log")]
8 use 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
23 pub trait SubscriberInitExt
24 where
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")))]
set_default(self) -> dispatcher::DefaultGuard38     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
try_init(self) -> Result<(), TryInitError>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
init(self)89     fn init(self) {
90         self.try_init()
91             .expect("failed to set global default subscriber")
92     }
93 }
94 
95 impl<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.
98 pub 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 
108 impl TryInitError {
109     #[cfg(feature = "std")]
new(e: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self110     fn new(e: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
111         Self { inner: e.into() }
112     }
113 
114     #[cfg(not(feature = "std"))]
new<T>(_: T) -> Self115     fn new<T>(_: T) -> Self {
116         Self { _p: () }
117     }
118 }
119 
120 impl fmt::Debug for TryInitError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result121     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 
134 impl fmt::Display for TryInitError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result135     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")]
149 impl Error for TryInitError {
source(&self) -> Option<&(dyn Error + 'static)>150     fn source(&self) -> Option<&(dyn Error + 'static)> {
151         self.inner.source()
152     }
153 }
154