Coverage Report

Created: 2026-07-16 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}