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}
154