/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-log-0.1.4/src/log_tracer.rs
Line | Count | Source |
1 | | //! An adapter for converting [`log`] records into `tracing` `Event`s. |
2 | | //! |
3 | | //! This module provides the [`LogTracer`] type which implements `log`'s [logger |
4 | | //! interface] by recording log records as `tracing` `Event`s. This is intended for |
5 | | //! use in conjunction with a `tracing` `Subscriber` to consume events from |
6 | | //! dependencies that emit [`log`] records within a trace context. |
7 | | //! |
8 | | //! # Usage |
9 | | //! |
10 | | //! To create and initialize a `LogTracer` with the default configurations, use: |
11 | | //! |
12 | | //! * [`init`] if you want to convert all logs, regardless of log level, |
13 | | //! allowing the tracing `Subscriber` to perform any filtering |
14 | | //! * [`init_with_filter`] to convert all logs up to a specified log level |
15 | | //! |
16 | | //! In addition, a [builder] is available for cases where more advanced |
17 | | //! configuration is required. In particular, the builder can be used to [ignore |
18 | | //! log records][ignore] emitted by particular crates. This is useful in cases |
19 | | //! such as when a crate emits both `tracing` diagnostics _and_ log records by |
20 | | //! default. |
21 | | //! |
22 | | //! [logger interface]: log::Log |
23 | | //! [`init`]: LogTracer.html#method.init |
24 | | //! [`init_with_filter`]: LogTracer.html#method.init_with_filter |
25 | | //! [builder]: LogTracer::builder() |
26 | | //! [ignore]: Builder::ignore_crate() |
27 | | use crate::AsTrace; |
28 | | pub use log::SetLoggerError; |
29 | | use tracing_core::dispatcher; |
30 | | |
31 | | /// A simple "logger" that converts all log records into `tracing` `Event`s. |
32 | | #[derive(Debug)] |
33 | | pub struct LogTracer { |
34 | | ignore_crates: Box<[String]>, |
35 | | } |
36 | | |
37 | | /// Configures a new `LogTracer`. |
38 | | #[derive(Debug)] |
39 | | pub struct Builder { |
40 | | ignore_crates: Vec<String>, |
41 | | filter: log::LevelFilter, |
42 | | #[cfg(all(feature = "interest-cache", feature = "std"))] |
43 | | interest_cache_config: Option<crate::InterestCacheConfig>, |
44 | | } |
45 | | |
46 | | // ===== impl LogTracer ===== |
47 | | |
48 | | impl LogTracer { |
49 | | /// Returns a builder that allows customizing a `LogTracer` and setting it |
50 | | /// the default logger. |
51 | | /// |
52 | | /// For example: |
53 | | /// ```rust |
54 | | /// # use std::error::Error; |
55 | | /// use tracing_log::LogTracer; |
56 | | /// use log; |
57 | | /// |
58 | | /// # fn main() -> Result<(), Box<Error>> { |
59 | | /// LogTracer::builder() |
60 | | /// .ignore_crate("foo") // suppose the `foo` crate is using `tracing`'s log feature |
61 | | /// .with_max_level(log::LevelFilter::Info) |
62 | | /// .init()?; |
63 | | /// |
64 | | /// // will be available for Subscribers as a tracing Event |
65 | | /// log::info!("an example info log"); |
66 | | /// # Ok(()) |
67 | | /// # } |
68 | | /// ``` |
69 | 0 | pub fn builder() -> Builder { |
70 | 0 | Builder::default() |
71 | 0 | } |
72 | | |
73 | | /// Creates a new `LogTracer` that can then be used as a logger for the `log` crate. |
74 | | /// |
75 | | /// It is generally simpler to use the [`init`] or [`init_with_filter`] methods |
76 | | /// which will create the `LogTracer` and set it as the global logger. |
77 | | /// |
78 | | /// Logger setup without the initialization methods can be done with: |
79 | | /// |
80 | | /// ```rust |
81 | | /// # use std::error::Error; |
82 | | /// use tracing_log::LogTracer; |
83 | | /// use log; |
84 | | /// |
85 | | /// # fn main() -> Result<(), Box<Error>> { |
86 | | /// let logger = LogTracer::new(); |
87 | | /// log::set_boxed_logger(Box::new(logger))?; |
88 | | /// log::set_max_level(log::LevelFilter::Trace); |
89 | | /// |
90 | | /// // will be available for Subscribers as a tracing Event |
91 | | /// log::trace!("an example trace log"); |
92 | | /// # Ok(()) |
93 | | /// # } |
94 | | /// ``` |
95 | | /// |
96 | | /// [`init`]: LogTracer::init() |
97 | | /// [`init_with_filter`]: .#method.init_with_filter |
98 | 0 | pub fn new() -> Self { |
99 | 0 | Self { |
100 | 0 | ignore_crates: Vec::new().into_boxed_slice(), |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | /// Sets up `LogTracer` as global logger for the `log` crate, |
105 | | /// with the given level as max level filter. |
106 | | /// |
107 | | /// Setting a global logger can only be done once. |
108 | | /// |
109 | | /// The [`builder`] function can be used to customize the `LogTracer` before |
110 | | /// initializing it. |
111 | | /// |
112 | | /// [`builder`]: LogTracer::builder() |
113 | | #[cfg(feature = "std")] |
114 | | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
115 | 0 | pub fn init_with_filter(level: log::LevelFilter) -> Result<(), SetLoggerError> { |
116 | 0 | Self::builder().with_max_level(level).init() |
117 | 0 | } |
118 | | |
119 | | /// Sets a `LogTracer` as the global logger for the `log` crate. |
120 | | /// |
121 | | /// Setting a global logger can only be done once. |
122 | | /// |
123 | | /// ```rust |
124 | | /// # use std::error::Error; |
125 | | /// use tracing_log::LogTracer; |
126 | | /// use log; |
127 | | /// |
128 | | /// # fn main() -> Result<(), Box<Error>> { |
129 | | /// LogTracer::init()?; |
130 | | /// |
131 | | /// // will be available for Subscribers as a tracing Event |
132 | | /// log::trace!("an example trace log"); |
133 | | /// # Ok(()) |
134 | | /// # } |
135 | | /// ``` |
136 | | /// |
137 | | /// This will forward all logs to `tracing` and lets the current `Subscriber` |
138 | | /// determine if they are enabled. |
139 | | /// |
140 | | /// The [`builder`] function can be used to customize the `LogTracer` before |
141 | | /// initializing it. |
142 | | /// |
143 | | /// If you know in advance you want to filter some log levels, |
144 | | /// use [`builder`] or [`init_with_filter`] instead. |
145 | | /// |
146 | | /// [`init_with_filter`]: LogTracer::init_with_filter() |
147 | | /// [`builder`]: LogTracer::builder() |
148 | | #[cfg(feature = "std")] |
149 | | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
150 | 0 | pub fn init() -> Result<(), SetLoggerError> { |
151 | 0 | Self::builder().init() |
152 | 0 | } |
153 | | } |
154 | | |
155 | | impl Default for LogTracer { |
156 | 0 | fn default() -> Self { |
157 | 0 | Self::new() |
158 | 0 | } |
159 | | } |
160 | | |
161 | | #[cfg(all(feature = "interest-cache", feature = "std"))] |
162 | | use crate::interest_cache::try_cache as try_cache_interest; |
163 | | |
164 | | #[cfg(not(all(feature = "interest-cache", feature = "std")))] |
165 | 0 | fn try_cache_interest(_: &log::Metadata<'_>, callback: impl FnOnce() -> bool) -> bool { |
166 | 0 | callback() |
167 | 0 | } |
168 | | |
169 | | impl log::Log for LogTracer { |
170 | 0 | fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { |
171 | | // First, check the log record against the current max level enabled by |
172 | | // the current `tracing` subscriber. |
173 | 0 | if metadata.level().as_trace() > tracing_core::LevelFilter::current() { |
174 | | // If the log record's level is above that, disable it. |
175 | 0 | return false; |
176 | 0 | } |
177 | | |
178 | | // Okay, it wasn't disabled by the max level — do we have any specific |
179 | | // modules to ignore? |
180 | 0 | if !self.ignore_crates.is_empty() { |
181 | | // If we are ignoring certain module paths, ensure that the metadata |
182 | | // does not start with one of those paths. |
183 | 0 | let target = metadata.target(); |
184 | 0 | for ignored in &self.ignore_crates[..] { |
185 | 0 | if target.starts_with(ignored) { |
186 | 0 | return false; |
187 | 0 | } |
188 | | } |
189 | 0 | } |
190 | | |
191 | 0 | try_cache_interest(metadata, || { |
192 | | // Finally, check if the current `tracing` dispatcher cares about this. |
193 | 0 | dispatcher::get_default(|dispatch| dispatch.enabled(&metadata.as_trace())) |
194 | 0 | }) |
195 | 0 | } |
196 | | |
197 | 0 | fn log(&self, record: &log::Record<'_>) { |
198 | 0 | if self.enabled(record.metadata()) { |
199 | 0 | crate::dispatch_record(record); |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | 0 | fn flush(&self) {} |
204 | | } |
205 | | |
206 | | // ===== impl Builder ===== |
207 | | |
208 | | impl Builder { |
209 | | /// Returns a new `Builder` to construct a [`LogTracer`]. |
210 | | /// |
211 | 0 | pub fn new() -> Self { |
212 | 0 | Self::default() |
213 | 0 | } |
214 | | |
215 | | /// Sets a global maximum level for `log` records. |
216 | | /// |
217 | | /// Log records whose level is more verbose than the provided level will be |
218 | | /// disabled. |
219 | | /// |
220 | | /// By default, all `log` records will be enabled. |
221 | 0 | pub fn with_max_level(self, filter: impl Into<log::LevelFilter>) -> Self { |
222 | 0 | let filter = filter.into(); |
223 | 0 | Self { filter, ..self } |
224 | 0 | } |
225 | | |
226 | | /// Configures the `LogTracer` to ignore all log records whose target |
227 | | /// starts with the given string. |
228 | | /// |
229 | | /// This should be used when a crate enables the `tracing/log` feature to |
230 | | /// emit log records for tracing events. Otherwise, those events will be |
231 | | /// recorded twice. |
232 | 0 | pub fn ignore_crate(mut self, name: impl Into<String>) -> Self { |
233 | 0 | self.ignore_crates.push(name.into()); |
234 | 0 | self |
235 | 0 | } |
236 | | |
237 | | /// Configures the `LogTracer` to ignore all log records whose target |
238 | | /// starts with any of the given the given strings. |
239 | | /// |
240 | | /// This should be used when a crate enables the `tracing/log` feature to |
241 | | /// emit log records for tracing events. Otherwise, those events will be |
242 | | /// recorded twice. |
243 | 0 | pub fn ignore_all<I>(self, crates: impl IntoIterator<Item = I>) -> Self |
244 | 0 | where |
245 | 0 | I: Into<String>, |
246 | | { |
247 | 0 | crates.into_iter().fold(self, Self::ignore_crate) |
248 | 0 | } |
249 | | |
250 | | /// Configures the `LogTracer` to either disable or enable the interest cache. |
251 | | /// |
252 | | /// When enabled, a per-thread LRU cache will be used to cache whenever the logger |
253 | | /// is interested in a given [level] + [target] pair for records generated through |
254 | | /// the `log` crate. |
255 | | /// |
256 | | /// When no `trace!` logs are enabled the logger is able to cheaply filter |
257 | | /// them out just by comparing their log level to the globally specified |
258 | | /// maximum, and immediately reject them. When *any* other `trace!` log is |
259 | | /// enabled (even one which doesn't actually exist!) the logger has to run |
260 | | /// its full filtering machinery on each and every `trace!` log, which can |
261 | | /// potentially be very expensive. |
262 | | /// |
263 | | /// Enabling this cache is useful in such situations to improve performance. |
264 | | /// |
265 | | /// You most likely do not want to enabled this if you have registered any dynamic |
266 | | /// filters on your logger and you want them to be run every time. |
267 | | /// |
268 | | /// This is disabled by default. |
269 | | /// |
270 | | /// [level]: log::Metadata::level |
271 | | /// [target]: log::Metadata::target |
272 | | #[cfg(all(feature = "interest-cache", feature = "std"))] |
273 | | #[cfg_attr(docsrs, doc(cfg(all(feature = "interest-cache", feature = "std"))))] |
274 | | pub fn with_interest_cache(mut self, config: crate::InterestCacheConfig) -> Self { |
275 | | self.interest_cache_config = Some(config); |
276 | | self |
277 | | } |
278 | | |
279 | | /// Constructs a new `LogTracer` with the provided configuration and sets it |
280 | | /// as the default logger. |
281 | | /// |
282 | | /// Setting a global logger can only be done once. |
283 | | #[cfg(feature = "std")] |
284 | | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] |
285 | | #[allow(unused_mut)] |
286 | 0 | pub fn init(mut self) -> Result<(), SetLoggerError> { |
287 | | #[cfg(all(feature = "interest-cache", feature = "std"))] |
288 | | crate::interest_cache::configure(self.interest_cache_config.take()); |
289 | | |
290 | 0 | let ignore_crates = self.ignore_crates.into_boxed_slice(); |
291 | 0 | let logger = Box::new(LogTracer { ignore_crates }); |
292 | 0 | log::set_boxed_logger(logger)?; |
293 | 0 | log::set_max_level(self.filter); |
294 | 0 | Ok(()) |
295 | 0 | } |
296 | | } |
297 | | |
298 | | impl Default for Builder { |
299 | 0 | fn default() -> Self { |
300 | 0 | Self { |
301 | 0 | ignore_crates: Vec::new(), |
302 | 0 | filter: log::LevelFilter::max(), |
303 | 0 | #[cfg(all(feature = "interest-cache", feature = "std"))] |
304 | 0 | interest_cache_config: None, |
305 | 0 | } |
306 | 0 | } |
307 | | } |