Coverage Report

Created: 2026-06-01 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-tree-0.2.5/src/format.rs
Line
Count
Source
1
use nu_ansi_term::Color;
2
use std::{
3
    fmt::{self, Write as _},
4
    io,
5
};
6
use tracing_core::{
7
    field::{Field, Visit},
8
    span, Level,
9
};
10
11
pub(crate) const LINE_VERT: &str = "│";
12
const LINE_HORIZ: &str = "─";
13
pub(crate) const LINE_BRANCH: &str = "├";
14
pub(crate) const LINE_CLOSE: &str = "┘";
15
pub(crate) const LINE_OPEN: &str = "┐";
16
17
#[derive(Debug, Copy, Clone)]
18
pub(crate) enum SpanMode {
19
    /// Executed on the parent before entering a child span
20
    PreOpen,
21
    Open {
22
        verbose: bool,
23
    },
24
    Close {
25
        verbose: bool,
26
    },
27
    /// A span has been entered but another *different* span has been entered in the meantime.
28
    Retrace {
29
        verbose: bool,
30
    },
31
    PostClose,
32
    Event,
33
}
34
35
#[derive(Debug)]
36
pub struct Config {
37
    /// Whether to use colors.
38
    pub ansi: bool,
39
    /// Whether an ascii art tree is used or (if false) whether to just use whitespace indent
40
    pub indent_lines: bool,
41
    /// The amount of chars to indent.
42
    pub indent_amount: usize,
43
    /// Whether to show the module paths.
44
    pub targets: bool,
45
    /// Whether to show thread ids.
46
    pub render_thread_ids: bool,
47
    /// Whether to show thread names.
48
    pub render_thread_names: bool,
49
    /// Specifies after how many indentation levels we will wrap back around to zero
50
    pub wraparound: usize,
51
    /// Whether to print the current span before activating a new one
52
    pub verbose_entry: bool,
53
    /// Whether to print the current span before exiting it.
54
    pub verbose_exit: bool,
55
    /// Print the path leading up to a span if a different span was entered concurrently
56
    pub span_retrace: bool,
57
    /// Whether to print squiggly brackets (`{}`) around the list of fields in a span.
58
    pub bracketed_fields: bool,
59
    /// Defer printing a span until an event is generated inside of it
60
    pub deferred_spans: bool,
61
    /// Print a label of the span mode (open/close etc).
62
    pub span_modes: bool,
63
    /// Whether to print the time with higher precision.
64
    pub higher_precision: bool,
65
}
66
67
impl Config {
68
0
    pub fn with_ansi(self, ansi: bool) -> Self {
69
0
        Self { ansi, ..self }
70
0
    }
71
72
0
    pub fn with_indent_lines(self, indent_lines: bool) -> Self {
73
0
        Self {
74
0
            indent_lines,
75
0
            ..self
76
0
        }
77
0
    }
78
79
0
    pub fn with_targets(self, targets: bool) -> Self {
80
0
        Self { targets, ..self }
81
0
    }
82
83
0
    pub fn with_thread_ids(self, render_thread_ids: bool) -> Self {
84
0
        Self {
85
0
            render_thread_ids,
86
0
            ..self
87
0
        }
88
0
    }
89
90
0
    pub fn with_thread_names(self, render_thread_names: bool) -> Self {
91
0
        Self {
92
0
            render_thread_names,
93
0
            ..self
94
0
        }
95
0
    }
96
97
0
    pub fn with_wraparound(self, wraparound: usize) -> Self {
98
0
        Self { wraparound, ..self }
99
0
    }
100
101
0
    pub fn with_verbose_entry(self, verbose_entry: bool) -> Self {
102
0
        Self {
103
0
            verbose_entry,
104
0
            ..self
105
0
        }
106
0
    }
107
108
0
    pub fn with_verbose_exit(self, verbose_exit: bool) -> Self {
109
0
        Self {
110
0
            verbose_exit,
111
0
            ..self
112
0
        }
113
0
    }
114
115
0
    pub fn with_span_retrace(self, enabled: bool) -> Self {
116
0
        Self {
117
0
            span_retrace: enabled,
118
0
            ..self
119
0
        }
120
0
    }
121
122
0
    pub fn with_deferred_spans(self, enable: bool) -> Self {
123
0
        Self {
124
0
            deferred_spans: enable,
125
0
            ..self
126
0
        }
127
0
    }
128
129
0
    pub fn with_span_modes(self, enable: bool) -> Self {
130
0
        Self {
131
0
            span_modes: enable,
132
0
            ..self
133
0
        }
134
0
    }
135
136
0
    pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self {
137
0
        Self {
138
0
            bracketed_fields,
139
0
            ..self
140
0
        }
141
0
    }
142
143
0
    pub fn with_higher_precision(self, higher_precision: bool) -> Self {
144
0
        Self {
145
0
            higher_precision,
146
0
            ..self
147
0
        }
148
0
    }
149
150
0
    pub(crate) fn prefix(&self) -> String {
151
0
        let mut buf = String::new();
152
0
        if self.render_thread_ids {
153
0
            write!(buf, "{:?}", std::thread::current().id()).unwrap();
154
0
            if buf.ends_with(')') {
155
0
                buf.truncate(buf.len() - 1);
156
0
            }
157
0
            if buf.starts_with("ThreadId(") {
158
0
                buf.drain(0.."ThreadId(".len());
159
0
            }
160
0
        }
161
0
        if self.render_thread_names {
162
0
            if let Some(name) = std::thread::current().name() {
163
0
                if self.render_thread_ids {
164
0
                    buf.push(':');
165
0
                }
166
0
                buf.push_str(name);
167
0
            }
168
0
        }
169
0
        buf
170
0
    }
171
}
172
173
impl Default for Config {
174
0
    fn default() -> Self {
175
0
        Self {
176
0
            ansi: true,
177
0
            indent_lines: false,
178
0
            indent_amount: 2,
179
0
            targets: false,
180
0
            render_thread_ids: false,
181
0
            render_thread_names: false,
182
0
            wraparound: usize::max_value(),
183
0
            verbose_entry: false,
184
0
            verbose_exit: false,
185
0
            span_retrace: false,
186
0
            bracketed_fields: false,
187
0
            deferred_spans: false,
188
0
            span_modes: false,
189
0
            higher_precision: false,
190
0
        }
191
0
    }
192
}
193
194
#[derive(Debug)]
195
pub struct Buffers {
196
    pub current_buf: String,
197
    pub indent_buf: String,
198
199
    /// The last seen span of this layer
200
    ///
201
    /// This serves to serialize spans as two events can be generated in different spans
202
    /// without the spans entering and exiting beforehand. This happens for multithreaded code
203
    /// and instrumented futures
204
    pub current_span: Option<span::Id>,
205
}
206
207
impl Buffers {
208
0
    pub fn new() -> Self {
209
0
        Self {
210
0
            current_buf: String::new(),
211
0
            indent_buf: String::new(),
212
0
            current_span: None,
213
0
        }
214
0
    }
215
216
0
    pub fn flush_current_buf(&mut self, mut writer: impl io::Write) {
217
0
        write!(writer, "{}", &self.current_buf).unwrap();
218
0
        self.current_buf.clear();
219
0
    }
Unexecuted instantiation: <tracing_tree::format::Buffers>::flush_current_buf::<tracing_subscriber::fmt::writer::TestWriter>
Unexecuted instantiation: <tracing_tree::format::Buffers>::flush_current_buf::<_>
220
221
0
    pub fn flush_indent_buf(&mut self) {
222
0
        self.current_buf.push_str(&self.indent_buf);
223
0
        self.indent_buf.clear();
224
0
    }
225
226
0
    pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) {
227
0
        let prefix = config.prefix();
228
229
        // Render something when wraparound occurs so the user is aware of it
230
0
        if config.indent_lines {
231
0
            self.current_buf.push('\n');
232
233
0
            match style {
234
                SpanMode::Close { .. } | SpanMode::PostClose => {
235
0
                    if indent > 0 && (indent + 1) % config.wraparound == 0 {
236
0
                        self.indent_buf.push_str(&prefix);
237
0
                        for _ in 0..(indent % config.wraparound * config.indent_amount) {
238
0
                            self.indent_buf.push_str(LINE_HORIZ);
239
0
                        }
240
0
                        self.indent_buf.push_str(LINE_OPEN);
241
0
                        self.indent_buf.push('\n');
242
0
                    }
243
                }
244
0
                _ => {}
245
            }
246
0
        }
247
248
0
        indent_block(
249
0
            &mut self.current_buf,
250
0
            &mut self.indent_buf,
251
0
            indent % config.wraparound,
252
0
            config.indent_amount,
253
0
            config.indent_lines,
254
0
            &prefix,
255
0
            style,
256
        );
257
258
0
        self.current_buf.clear();
259
0
        self.flush_indent_buf();
260
261
        // Render something when wraparound occurs so the user is aware of it
262
0
        if config.indent_lines {
263
0
            match style {
264
                SpanMode::PreOpen { .. } | SpanMode::Open { .. } => {
265
0
                    if indent > 0 && (indent + 1) % config.wraparound == 0 {
266
0
                        self.current_buf.push_str(&prefix);
267
0
                        for _ in 0..(indent % config.wraparound * config.indent_amount) {
268
0
                            self.current_buf.push_str(LINE_HORIZ);
269
0
                        }
270
0
                        self.current_buf.push_str(LINE_CLOSE);
271
0
                        self.current_buf.push('\n');
272
0
                    }
273
                }
274
0
                _ => {}
275
            }
276
0
        }
277
0
    }
278
}
279
280
pub struct FmtEvent<'a> {
281
    pub bufs: &'a mut Buffers,
282
    pub comma: bool,
283
}
284
285
impl<'a> Visit for FmtEvent<'a> {
286
0
    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
287
0
        let buf = &mut self.bufs.current_buf;
288
0
        let comma = if self.comma { "," } else { "" };
289
0
        match field.name() {
290
0
            "message" => {
291
0
                write!(buf, "{} {:?}", comma, value).unwrap();
292
0
                self.comma = true;
293
0
            }
294
            // Skip fields that are actually log metadata that have already been handled
295
            #[cfg(feature = "tracing-log")]
296
0
            name if name.starts_with("log.") => {}
297
0
            name => {
298
0
                write!(buf, "{} {}={:?}", comma, name, value).unwrap();
299
0
                self.comma = true;
300
0
            }
301
        }
302
0
    }
303
}
304
305
pub struct ColorLevel<'a>(pub &'a Level);
306
307
impl<'a> fmt::Display for ColorLevel<'a> {
308
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309
0
        match *self.0 {
310
0
            Level::TRACE => Color::Purple.bold().paint("TRACE"),
311
0
            Level::DEBUG => Color::Blue.bold().paint("DEBUG"),
312
0
            Level::INFO => Color::Green.bold().paint(" INFO"),
313
0
            Level::WARN => Color::Rgb(252, 234, 160).bold().paint(" WARN"), // orange
314
0
            Level::ERROR => Color::Red.bold().paint("ERROR"),
315
        }
316
0
        .fmt(f)
317
0
    }
318
}
319
320
0
pub(crate) fn write_span_mode(buf: &mut String, style: SpanMode) {
321
0
    match style {
322
0
        SpanMode::Open { verbose: true } => buf.push_str("open(v)"),
323
0
        SpanMode::Open { verbose: false } => buf.push_str("open"),
324
0
        SpanMode::Retrace { verbose: false } => buf.push_str("retrace"),
325
0
        SpanMode::Retrace { verbose: true } => buf.push_str("retrace(v)"),
326
0
        SpanMode::Close { verbose: true } => buf.push_str("close(v)"),
327
0
        SpanMode::Close { verbose: false } => buf.push_str("close"),
328
0
        SpanMode::PreOpen => buf.push_str("pre_open"),
329
0
        SpanMode::PostClose => buf.push_str("post_close"),
330
0
        SpanMode::Event => buf.push_str("event"),
331
    }
332
333
0
    buf.push_str(": ")
334
0
}
335
336
0
fn indent_block_with_lines(
337
0
    lines: &[&str],
338
0
    buf: &mut String,
339
0
    indent: usize,
340
0
    // width of one level of indent
341
0
    indent_amount: usize,
342
0
    prefix: &str,
343
0
    style: SpanMode,
344
0
) {
345
0
    let indent_spaces = indent * indent_amount;
346
347
0
    if lines.is_empty() {
348
0
        return;
349
0
    } else if indent_spaces == 0 {
350
0
        for line in lines {
351
0
            buf.push_str(prefix);
352
            // The first indent is special, we only need to print open/close and nothing else
353
0
            if indent == 0 {
354
0
                match style {
355
0
                    SpanMode::Open { .. } => buf.push_str(LINE_OPEN),
356
0
                    SpanMode::Retrace { .. } => buf.push_str(LINE_OPEN),
357
0
                    SpanMode::Close { .. } => buf.push_str(LINE_CLOSE),
358
0
                    SpanMode::PreOpen { .. } | SpanMode::PostClose => {}
359
0
                    SpanMode::Event => {}
360
                }
361
0
            }
362
0
            buf.push_str(line);
363
0
            buf.push('\n');
364
        }
365
0
        return;
366
0
    }
367
368
0
    let mut s = String::with_capacity(indent_spaces + prefix.len());
369
0
    s.push_str(prefix);
370
371
    // instead of using all spaces to indent, draw a vertical line at every indent level
372
    // up until the last indent
373
0
    for i in 0..(indent_spaces - indent_amount) {
374
0
        if i % indent_amount == 0 {
375
0
            s.push_str(LINE_VERT);
376
0
        } else {
377
0
            s.push(' ');
378
0
        }
379
    }
380
381
    // draw branch
382
0
    buf.push_str(&s);
383
384
0
    match style {
385
        SpanMode::PreOpen => {
386
0
            buf.push_str(LINE_BRANCH);
387
0
            for _ in 1..(indent_amount / 2) {
388
0
                buf.push_str(LINE_HORIZ);
389
0
            }
390
0
            buf.push_str(LINE_OPEN);
391
        }
392
        SpanMode::Open { verbose: false } | SpanMode::Retrace { verbose: false } => {
393
0
            buf.push_str(LINE_BRANCH);
394
0
            for _ in 1..indent_amount {
395
0
                buf.push_str(LINE_HORIZ);
396
0
            }
397
0
            buf.push_str(LINE_OPEN);
398
        }
399
        SpanMode::Open { verbose: true } | SpanMode::Retrace { verbose: true } => {
400
0
            buf.push_str(LINE_VERT);
401
0
            for _ in 1..(indent_amount / 2) {
402
0
                buf.push(' ');
403
0
            }
404
            // We don't have the space for fancy rendering at single space indent.
405
0
            if indent_amount > 1 {
406
0
                buf.push('└');
407
0
            }
408
0
            for _ in (indent_amount / 2)..(indent_amount - 1) {
409
0
                buf.push_str(LINE_HORIZ);
410
0
            }
411
            // We don't have the space for fancy rendering at single space indent.
412
0
            if indent_amount > 1 {
413
0
                buf.push_str(LINE_OPEN);
414
0
            } else {
415
0
                buf.push_str(LINE_VERT);
416
0
            }
417
        }
418
        SpanMode::Close { verbose: false } => {
419
0
            buf.push_str(LINE_BRANCH);
420
0
            for _ in 1..indent_amount {
421
0
                buf.push_str(LINE_HORIZ);
422
0
            }
423
0
            buf.push_str(LINE_CLOSE);
424
        }
425
        SpanMode::Close { verbose: true } => {
426
0
            buf.push_str(LINE_VERT);
427
0
            for _ in 1..(indent_amount / 2) {
428
0
                buf.push(' ');
429
0
            }
430
            // We don't have the space for fancy rendering at single space indent.
431
0
            if indent_amount > 1 {
432
0
                buf.push('┌');
433
0
            }
434
0
            for _ in (indent_amount / 2)..(indent_amount - 1) {
435
0
                buf.push_str(LINE_HORIZ);
436
0
            }
437
            // We don't have the space for fancy rendering at single space indent.
438
0
            if indent_amount > 1 {
439
0
                buf.push_str(LINE_CLOSE);
440
0
            } else {
441
0
                buf.push_str(LINE_VERT);
442
0
            }
443
        }
444
        SpanMode::PostClose => {
445
0
            buf.push_str(LINE_BRANCH);
446
0
            for _ in 1..(indent_amount / 2) {
447
0
                buf.push_str(LINE_HORIZ);
448
0
            }
449
0
            buf.push_str(LINE_CLOSE);
450
        }
451
        SpanMode::Event => {
452
0
            buf.push_str(LINE_BRANCH);
453
454
            // add `indent_amount - 1` horizontal lines before the span/event
455
0
            for _ in 0..(indent_amount - 1) {
456
0
                buf.push_str(LINE_HORIZ);
457
0
            }
458
        }
459
    }
460
0
    buf.push_str(lines[0]);
461
0
    buf.push('\n');
462
463
    // add the rest of the indentation, since we don't want to draw horizontal lines
464
    // for subsequent lines
465
0
    for i in 0..indent_amount {
466
0
        if i % indent_amount == 0 {
467
0
            s.push_str(LINE_VERT);
468
0
        } else {
469
0
            s.push(' ');
470
0
        }
471
    }
472
473
    // add all of the actual content, with each line preceded by the indent string
474
0
    for line in &lines[1..] {
475
0
        buf.push_str(&s);
476
0
        buf.push_str(line);
477
0
        buf.push('\n');
478
0
    }
479
0
}
480
481
0
fn indent_block(
482
0
    block: &mut String,
483
0
    buf: &mut String,
484
0
    mut indent: usize,
485
0
    indent_amount: usize,
486
0
    indent_lines: bool,
487
0
    prefix: &str,
488
0
    style: SpanMode,
489
0
) {
490
0
    let lines: Vec<&str> = block.lines().collect();
491
0
    let indent_spaces = indent * indent_amount;
492
0
    buf.reserve(block.len() + (lines.len() * indent_spaces));
493
494
    // The PreOpen and PostClose need to match up with the indent of the entered child span one more indent
495
    // deep
496
0
    match style {
497
0
        SpanMode::PreOpen | SpanMode::PostClose => {
498
0
            indent += 1;
499
0
        }
500
0
        _ => (),
501
    }
502
503
0
    if indent_lines {
504
0
        indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style);
505
0
    } else {
506
0
        let indent_str = String::from(" ").repeat(indent_spaces);
507
0
        for line in lines {
508
0
            buf.push_str(prefix);
509
0
            buf.push(' ');
510
0
            buf.push_str(&indent_str);
511
0
            buf.push_str(line);
512
0
            buf.push('\n');
513
0
        }
514
    }
515
0
}