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/trace_logger.rs
Line
Count
Source
1
//! A `tracing` [`Subscriber`] that uses the [`log`] crate as a backend for
2
//! formatting `tracing` spans and events.
3
//!
4
//! When a [`TraceLogger`] is set as the current subscriber, it will record
5
//! traces by emitting [`log::Record`]s that can be collected by a logger.
6
//!
7
//! **Note**: This API has been deprecated since version 0.1.1. In order to emit
8
//! `tracing` events as `log` records, the ["log" and "log-always" feature
9
//! flags][flags] on the `tracing` crate should be used instead.
10
//!
11
//! [`log`]: log
12
//! [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html
13
//! [`log::Record`]:log::Record
14
//! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags
15
#![deprecated(
16
    since = "0.1.1",
17
    note = "use the `tracing` crate's \"log\" feature flag instead"
18
)]
19
use crate::AsLog;
20
use std::{
21
    cell::RefCell,
22
    collections::HashMap,
23
    fmt::{self, Write},
24
    sync::{
25
        atomic::{AtomicUsize, Ordering},
26
        Mutex,
27
    },
28
};
29
use tracing_core::{
30
    field,
31
    span::{self, Id},
32
    Event, Metadata, Subscriber,
33
};
34
35
/// A `tracing` [`Subscriber`] implementation that logs all recorded
36
/// trace events.
37
///
38
/// **Note**: This API has been deprecated since version 0.1.1. In order to emit
39
/// `tracing` events as `log` records, the ["log" and "log-always" feature
40
/// flags][flags] on the `tracing` crate should be used instead.
41
///
42
/// [`Subscriber`]: https://docs.rs/tracing/0.1.7/tracing/subscriber/trait.Subscriber.html
43
/// [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags
44
pub struct TraceLogger {
45
    settings: Builder,
46
    spans: Mutex<HashMap<Id, SpanLineBuilder>>,
47
    next_id: AtomicUsize,
48
}
49
50
thread_local! {
51
    static CURRENT: RefCell<Vec<Id>> = RefCell::new(Vec::new());
52
}
53
/// Configures and constructs a [`TraceLogger`].
54
///
55
#[derive(Debug)]
56
pub struct Builder {
57
    log_span_closes: bool,
58
    log_enters: bool,
59
    log_exits: bool,
60
    log_ids: bool,
61
    parent_fields: bool,
62
    log_parent: bool,
63
}
64
65
// ===== impl TraceLogger =====
66
67
impl TraceLogger {
68
    /// Returns a new `TraceLogger` with the default configuration.
69
0
    pub fn new() -> Self {
70
0
        Self::builder().finish()
71
0
    }
72
73
    /// Returns a `Builder` for configuring a `TraceLogger`.
74
0
    pub fn builder() -> Builder {
75
0
        Default::default()
76
0
    }
77
78
0
    fn from_builder(settings: Builder) -> Self {
79
0
        Self {
80
0
            settings,
81
0
            ..Default::default()
82
0
        }
83
0
    }
84
85
0
    fn next_id(&self) -> Id {
86
0
        Id::from_u64(self.next_id.fetch_add(1, Ordering::SeqCst) as u64)
87
0
    }
88
}
89
90
// ===== impl Builder =====
91
92
impl Builder {
93
    /// Configures whether or not the [`TraceLogger`] being constructed will log
94
    /// when a span closes.
95
    ///
96
0
    pub fn with_span_closes(self, log_span_closes: bool) -> Self {
97
0
        Self {
98
0
            log_span_closes,
99
0
            ..self
100
0
        }
101
0
    }
102
103
    /// Configures whether or not the [`TraceLogger`] being constructed will
104
    /// include the fields of parent spans when formatting events.
105
    ///
106
0
    pub fn with_parent_fields(self, parent_fields: bool) -> Self {
107
0
        Self {
108
0
            parent_fields,
109
0
            ..self
110
0
        }
111
0
    }
112
113
    /// Configures whether or not the [`TraceLogger`] being constructed will log
114
    /// when a span is entered.
115
    ///
116
    /// If this is set to false, fields from the current span will still be
117
    /// recorded as context, but the actual entry will not create a log record.
118
    ///
119
0
    pub fn with_span_entry(self, log_enters: bool) -> Self {
120
0
        Self { log_enters, ..self }
121
0
    }
122
123
    /// Configures whether or not the [`TraceLogger`] being constructed will log
124
    /// when a span is exited.
125
    ///
126
0
    pub fn with_span_exits(self, log_exits: bool) -> Self {
127
0
        Self { log_exits, ..self }
128
0
    }
129
130
    /// Configures whether or not the [`TraceLogger`] being constructed will
131
    /// include span IDs when formatting log output.
132
    ///
133
0
    pub fn with_ids(self, log_ids: bool) -> Self {
134
0
        Self { log_ids, ..self }
135
0
    }
136
137
    /// Configures whether or not the [`TraceLogger`] being constructed will
138
    /// include the names of parent spans as context when formatting events.
139
    ///
140
0
    pub fn with_parent_names(self, log_parent: bool) -> Self {
141
0
        Self { log_parent, ..self }
142
0
    }
143
144
    /// Complete the builder, returning a configured [`TraceLogger`].
145
    ///
146
0
    pub fn finish(self) -> TraceLogger {
147
0
        TraceLogger::from_builder(self)
148
0
    }
149
}
150
151
impl Default for Builder {
152
0
    fn default() -> Self {
153
0
        Builder {
154
0
            log_span_closes: false,
155
0
            parent_fields: true,
156
0
            log_exits: false,
157
0
            log_ids: false,
158
0
            log_parent: true,
159
0
            log_enters: false,
160
0
        }
161
0
    }
162
}
163
164
impl Default for TraceLogger {
165
0
    fn default() -> Self {
166
0
        TraceLogger {
167
0
            settings: Default::default(),
168
0
            spans: Default::default(),
169
0
            next_id: AtomicUsize::new(1),
170
0
        }
171
0
    }
172
}
173
174
#[derive(Debug)]
175
struct SpanLineBuilder {
176
    parent: Option<Id>,
177
    ref_count: usize,
178
    fields: String,
179
    file: Option<String>,
180
    line: Option<u32>,
181
    module_path: Option<String>,
182
    target: String,
183
    level: log::Level,
184
    name: &'static str,
185
}
186
187
impl SpanLineBuilder {
188
0
    fn new(parent: Option<Id>, meta: &Metadata<'_>, fields: String) -> Self {
189
0
        Self {
190
0
            parent,
191
0
            ref_count: 1,
192
0
            fields,
193
0
            file: meta.file().map(String::from),
194
0
            line: meta.line(),
195
0
            module_path: meta.module_path().map(String::from),
196
0
            target: String::from(meta.target()),
197
0
            level: meta.level().as_log(),
198
0
            name: meta.name(),
199
0
        }
200
0
    }
201
202
0
    fn log_meta(&self) -> log::Metadata<'_> {
203
0
        log::MetadataBuilder::new()
204
0
            .level(self.level)
205
0
            .target(self.target.as_ref())
206
0
            .build()
207
0
    }
208
209
0
    fn finish(self) {
210
0
        let log_meta = self.log_meta();
211
0
        let logger = log::logger();
212
0
        if logger.enabled(&log_meta) {
213
0
            logger.log(
214
0
                &log::Record::builder()
215
0
                    .metadata(log_meta)
216
0
                    .target(self.target.as_ref())
217
0
                    .module_path(self.module_path.as_ref().map(String::as_ref))
218
0
                    .file(self.file.as_ref().map(String::as_ref))
219
0
                    .line(self.line)
220
0
                    .args(format_args!("close {}; {}", self.name, self.fields))
221
0
                    .build(),
222
0
            );
223
0
        }
224
0
    }
225
}
226
227
impl field::Visit for SpanLineBuilder {
228
0
    fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
229
0
        write!(self.fields, " {}={:?};", field.name(), value)
230
0
            .expect("write to string should never fail")
231
0
    }
232
}
233
234
impl Subscriber for TraceLogger {
235
0
    fn enabled(&self, metadata: &Metadata<'_>) -> bool {
236
0
        log::logger().enabled(&metadata.as_log())
237
0
    }
238
239
0
    fn new_span(&self, attrs: &span::Attributes<'_>) -> Id {
240
0
        let id = self.next_id();
241
0
        let mut spans = self.spans.lock().unwrap();
242
0
        let mut fields = String::new();
243
0
        let parent = self.current_id();
244
0
        if self.settings.parent_fields {
245
0
            let mut next_parent = parent.as_ref();
246
0
            while let Some(parent) = next_parent.and_then(|p| spans.get(p)) {
247
0
                write!(&mut fields, "{}", parent.fields).expect("write to string cannot fail");
248
0
                next_parent = parent.parent.as_ref();
249
0
            }
250
0
        }
251
0
        let mut span = SpanLineBuilder::new(parent, attrs.metadata(), fields);
252
0
        attrs.record(&mut span);
253
0
        spans.insert(id.clone(), span);
254
0
        id
255
0
    }
256
257
0
    fn record(&self, span: &Id, values: &span::Record<'_>) {
258
0
        let mut spans = self.spans.lock().unwrap();
259
0
        if let Some(span) = spans.get_mut(span) {
260
0
            values.record(span);
261
0
        }
262
0
    }
263
264
0
    fn record_follows_from(&self, span: &Id, follows: &Id) {
265
        // TODO: this should eventually track the relationship?
266
0
        log::logger().log(
267
0
            &log::Record::builder()
268
0
                .level(log::Level::Trace)
269
0
                .args(format_args!("span {:?} follows_from={:?};", span, follows))
270
0
                .build(),
271
        );
272
0
    }
273
274
0
    fn enter(&self, id: &Id) {
275
0
        let _ = CURRENT.try_with(|current| {
276
0
            let mut current = current.borrow_mut();
277
0
            if current.contains(id) {
278
                // Ignore duplicate enters.
279
0
                return;
280
0
            }
281
0
            current.push(id.clone());
282
0
        });
283
0
        let spans = self.spans.lock().unwrap();
284
0
        if self.settings.log_enters {
285
0
            if let Some(span) = spans.get(id) {
286
0
                let log_meta = span.log_meta();
287
0
                let logger = log::logger();
288
0
                if logger.enabled(&log_meta) {
289
0
                    let current_id = self.current_id();
290
0
                    let current_fields = current_id
291
0
                        .as_ref()
292
0
                        .and_then(|id| spans.get(id))
293
0
                        .map(|span| span.fields.as_ref())
294
0
                        .unwrap_or("");
295
0
                    if self.settings.log_ids {
296
0
                        logger.log(
297
0
                            &log::Record::builder()
298
0
                                .metadata(log_meta)
299
0
                                .target(span.target.as_ref())
300
0
                                .module_path(span.module_path.as_ref().map(String::as_ref))
301
0
                                .file(span.file.as_ref().map(String::as_ref))
302
0
                                .line(span.line)
303
0
                                .args(format_args!(
304
0
                                    "enter {}; in={:?}; {}",
305
0
                                    span.name, current_id, current_fields
306
0
                                ))
307
0
                                .build(),
308
0
                        );
309
0
                    } else {
310
0
                        logger.log(
311
0
                            &log::Record::builder()
312
0
                                .metadata(log_meta)
313
0
                                .target(span.target.as_ref())
314
0
                                .module_path(span.module_path.as_ref().map(String::as_ref))
315
0
                                .file(span.file.as_ref().map(String::as_ref))
316
0
                                .line(span.line)
317
0
                                .args(format_args!("enter {}; {}", span.name, current_fields))
318
0
                                .build(),
319
0
                        );
320
0
                    }
321
0
                }
322
0
            }
323
0
        }
324
0
    }
325
326
0
    fn exit(&self, id: &Id) {
327
0
        let _ = CURRENT.try_with(|current| {
328
0
            let mut current = current.borrow_mut();
329
0
            if current.last() == Some(id) {
330
0
                current.pop()
331
            } else {
332
0
                None
333
            }
334
0
        });
335
0
        if self.settings.log_exits {
336
0
            let spans = self.spans.lock().unwrap();
337
0
            if let Some(span) = spans.get(id) {
338
0
                let log_meta = span.log_meta();
339
0
                let logger = log::logger();
340
0
                if logger.enabled(&log_meta) {
341
0
                    logger.log(
342
0
                        &log::Record::builder()
343
0
                            .metadata(log_meta)
344
0
                            .target(span.target.as_ref())
345
0
                            .module_path(span.module_path.as_ref().map(String::as_ref))
346
0
                            .file(span.file.as_ref().map(String::as_ref))
347
0
                            .line(span.line)
348
0
                            .args(format_args!("exit {}", span.name))
349
0
                            .build(),
350
0
                    );
351
0
                }
352
0
            }
353
0
        }
354
0
    }
355
356
0
    fn event(&self, event: &Event<'_>) {
357
0
        let meta = event.metadata();
358
0
        let log_meta = meta.as_log();
359
0
        let logger = log::logger();
360
0
        if logger.enabled(&log_meta) {
361
0
            let spans = self.spans.lock().unwrap();
362
0
            let current = self.current_id().and_then(|id| spans.get(&id));
363
0
            let (current_fields, parent) = current
364
0
                .map(|span| {
365
0
                    let fields = span.fields.as_ref();
366
0
                    let parent = if self.settings.log_parent {
367
0
                        Some(span.name)
368
                    } else {
369
0
                        None
370
                    };
371
0
                    (fields, parent)
372
0
                })
373
0
                .unwrap_or(("", None));
374
0
            logger.log(
375
0
                &log::Record::builder()
376
0
                    .metadata(log_meta)
377
0
                    .target(meta.target())
378
0
                    .module_path(meta.module_path().as_ref().cloned())
379
0
                    .file(meta.file().as_ref().cloned())
380
0
                    .line(meta.line())
381
0
                    .args(format_args!(
382
0
                        "{}{}{}{}",
383
0
                        parent.unwrap_or(""),
384
0
                        if parent.is_some() { ": " } else { "" },
385
0
                        LogEvent(event),
386
                        current_fields,
387
                    ))
388
0
                    .build(),
389
            );
390
0
        }
391
0
    }
392
393
0
    fn clone_span(&self, id: &Id) -> Id {
394
0
        let mut spans = self.spans.lock().unwrap();
395
0
        if let Some(span) = spans.get_mut(id) {
396
0
            span.ref_count += 1;
397
0
        }
398
0
        id.clone()
399
0
    }
400
401
0
    fn try_close(&self, id: Id) -> bool {
402
0
        let mut spans = self.spans.lock().unwrap();
403
0
        if spans.contains_key(&id) {
404
0
            if spans.get(&id).unwrap().ref_count == 1 {
405
0
                let span = spans.remove(&id).unwrap();
406
0
                if self.settings.log_span_closes {
407
0
                    span.finish();
408
0
                }
409
0
                return true;
410
0
            } else {
411
0
                spans.get_mut(&id).unwrap().ref_count -= 1;
412
0
            }
413
0
        }
414
0
        false
415
0
    }
416
}
417
418
impl TraceLogger {
419
    #[inline]
420
0
    fn current_id(&self) -> Option<Id> {
421
0
        CURRENT
422
0
            .try_with(|current| current.borrow().last().map(|span| self.clone_span(span)))
423
0
            .ok()?
424
0
    }
425
}
426
427
struct LogEvent<'a>(&'a Event<'a>);
428
429
impl<'a> fmt::Display for LogEvent<'a> {
430
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431
0
        let mut has_logged = false;
432
0
        let mut format_fields = |field: &field::Field, value: &dyn fmt::Debug| {
433
0
            let name = field.name();
434
0
            let leading = if has_logged { " " } else { "" };
435
            // TODO: handle fmt error?
436
0
            let _ = if name == "message" {
437
0
                write!(f, "{}{:?};", leading, value)
438
            } else {
439
0
                write!(f, "{}{}={:?};", leading, name, value)
440
            };
441
0
            has_logged = true;
442
0
        };
443
444
0
        self.0.record(&mut format_fields);
445
0
        Ok(())
446
0
    }
447
}
448
449
impl fmt::Debug for TraceLogger {
450
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451
0
        f.debug_struct("TraceLogger")
452
0
            .field("settings", &self.settings)
453
0
            .field("spans", &self.spans)
454
0
            .field("current", &self.current_id())
455
0
            .field("next_id", &self.next_id)
456
0
            .finish()
457
0
    }
458
}