tracing_tree2/
lib.rs

1pub(crate) mod format;
2
3use format::{Buffers, ColorLevel, Config, FmtEvent, SpanMode};
4use nu_ansi_term::{Color, Style};
5use std::{
6    fmt::{self, Write as _},
7    io,
8    sync::Mutex,
9    time::Instant,
10};
11use tracing_core::{
12    field::{Field, Visit},
13    span::{Attributes, Id},
14    Event, Subscriber,
15};
16#[cfg(feature = "tracing-log")]
17use tracing_log::NormalizeEvent;
18use tracing_subscriber::{
19    fmt::MakeWriter,
20    layer::{Context, Layer},
21    registry::{self, LookupSpan},
22};
23
24pub(crate) struct Data {
25    start: Instant,
26    kvs: Vec<(&'static str, String)>,
27}
28
29impl Data {
30    pub fn new(attrs: &Attributes<'_>) -> Self {
31        let mut span = Self {
32            start: Instant::now(),
33            kvs: Vec::new(),
34        };
35        attrs.record(&mut span);
36        span
37    }
38}
39
40impl Visit for Data {
41    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
42        self.kvs.push((field.name(), format!("{value:?}")))
43    }
44}
45#[derive(Debug)]
46pub struct HierarchicalLayer<W = fn() -> io::Stderr>
47where
48    W: for<'writer> MakeWriter<'writer> + 'static,
49{
50    make_writer: W,
51    bufs: Mutex<Buffers>,
52    config: Config,
53}
54
55impl Default for HierarchicalLayer {
56    fn default() -> Self {
57        Self::new(2)
58    }
59}
60
61impl HierarchicalLayer<fn() -> io::Stderr> {
62    pub fn new(indent_amount: usize) -> Self {
63        let ansi = atty::is(atty::Stream::Stderr);
64        let config = Config {
65            ansi,
66            indent_amount,
67            ..Default::default()
68        };
69        Self {
70            make_writer: io::stderr,
71            bufs: Mutex::new(Buffers::new()),
72            config,
73        }
74    }
75}
76
77impl<W> HierarchicalLayer<W>
78where
79    W: for<'writer> MakeWriter<'writer> + 'static,
80{
81    /// Enables terminal colors, boldness and italics.
82    pub fn with_ansi(self, ansi: bool) -> Self {
83        Self {
84            config: self.config.with_ansi(ansi),
85            ..self
86        }
87    }
88
89    pub fn with_writer<W2>(self, make_writer: W2) -> HierarchicalLayer<W2>
90    where
91        W2: for<'writer> MakeWriter<'writer>,
92    {
93        HierarchicalLayer {
94            make_writer,
95            config: self.config,
96            bufs: self.bufs,
97        }
98    }
99
100    pub fn with_indent_amount(self, indent_amount: usize) -> Self {
101        let config = Config {
102            indent_amount,
103            ..self.config
104        };
105        Self { config, ..self }
106    }
107
108    /// Renders an ascii art tree instead of just using whitespace indentation.
109    pub fn with_indent_lines(self, indent_lines: bool) -> Self {
110        Self {
111            config: self.config.with_indent_lines(indent_lines),
112            ..self
113        }
114    }
115
116    /// Whether to render the event and span targets. Usually targets are the module path to the
117    /// event/span macro invocation.
118    pub fn with_targets(self, targets: bool) -> Self {
119        Self {
120            config: self.config.with_targets(targets),
121            ..self
122        }
123    }
124
125    /// Whether to render the thread id in the beginning of every line. This is helpful to
126    /// untangle the tracing statements emitted by each thread.
127    pub fn with_thread_ids(self, thread_ids: bool) -> Self {
128        Self {
129            config: self.config.with_thread_ids(thread_ids),
130            ..self
131        }
132    }
133
134    /// Whether to render the thread name in the beginning of every line. Not all threads have
135    /// names, but if they do, this may be more helpful than the generic thread ids.
136    pub fn with_thread_names(self, thread_names: bool) -> Self {
137        Self {
138            config: self.config.with_thread_names(thread_names),
139            ..self
140        }
141    }
142
143    /// Resets the indentation to zero after `wraparound` indentation levels.
144    /// This is helpful if you expect very deeply nested spans as otherwise the indentation
145    /// just runs out of your screen.
146    pub fn with_wraparound(self, wraparound: usize) -> Self {
147        Self {
148            config: self.config.with_wraparound(wraparound),
149            ..self
150        }
151    }
152
153    /// Whether to print the currently active span's message again before entering a new span.
154    /// This helps if the entry to the current span was quite a while back (and with scrolling
155    /// upwards in logs).
156    pub fn with_verbose_entry(self, verbose_entry: bool) -> Self {
157        Self {
158            config: self.config.with_verbose_entry(verbose_entry),
159            ..self
160        }
161    }
162
163    /// Whether to print the currently active span's message again before dropping it.
164    /// This helps if the entry to the current span was quite a while back (and with scrolling
165    /// upwards in logs).
166    pub fn with_verbose_exit(self, verbose_exit: bool) -> Self {
167        Self {
168            config: self.config.with_verbose_exit(verbose_exit),
169            ..self
170        }
171    }
172
173    /// Whether to print `{}` around the fields when printing a span.
174    /// This can help visually distinguish fields from the rest of the message.
175    pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self {
176        Self {
177            config: self.config.with_bracketed_fields(bracketed_fields),
178            ..self
179        }
180    }
181
182    /// Whether to include the log level in the output.
183    pub fn with_level(self, level: bool) -> Self {
184        Self {
185            config: self.config.with_level(level),
186            ..self
187        }
188    }
189
190    fn styled(&self, style: Style, text: impl AsRef<str>) -> String {
191        if self.config.ansi {
192            style.paint(text.as_ref()).to_string()
193        } else {
194            text.as_ref().to_string()
195        }
196    }
197
198    fn print_kvs<'a, I, V>(&self, buf: &mut impl fmt::Write, kvs: I) -> fmt::Result
199    where
200        I: IntoIterator<Item = (&'a str, V)>,
201        V: fmt::Display + 'a,
202    {
203        let mut kvs = kvs.into_iter();
204        if let Some((k, v)) = kvs.next() {
205            if k == "message" {
206                write!(buf, "{v}")?;
207            } else {
208                write!(buf, "{k}={v}")?;
209            }
210        }
211        for (k, v) in kvs {
212            write!(buf, ", {k}={v}")?;
213        }
214        Ok(())
215    }
216
217    fn write_span_info<S>(&self, id: &Id, ctx: &Context<S>, style: SpanMode)
218    where
219        S: Subscriber + for<'span> LookupSpan<'span>,
220    {
221        let span = ctx
222            .span(id)
223            .expect("in on_enter/on_exit but span does not exist");
224        let ext = span.extensions();
225        let data = ext.get::<Data>().expect("span does not have data");
226
227        let mut guard = self.bufs.lock().unwrap();
228        let bufs = &mut *guard;
229        let mut current_buf = &mut bufs.current_buf;
230
231        let indent = ctx
232            .lookup_current()
233            .as_ref()
234            .map(registry::SpanRef::scope)
235            .map(registry::Scope::from_root)
236            .into_iter()
237            .flatten()
238            .count();
239
240        if self.config.verbose_entry || matches!(style, SpanMode::Open { .. } | SpanMode::Event) {
241            if self.config.targets {
242                let target = span.metadata().target();
243                write!(
244                    &mut current_buf,
245                    "{}::",
246                    self.styled(Style::new().dimmed(), target,),
247                )
248                .expect("Unable to write to buffer");
249            }
250
251            write!(
252                current_buf,
253                "{name}",
254                name = self.styled(Style::new().fg(Color::Green).bold(), span.metadata().name())
255            )
256            .unwrap();
257            if self.config.bracketed_fields {
258                write!(
259                    current_buf,
260                    "{}",
261                    self.styled(Style::new().fg(Color::Green).bold(), "{") // Style::new().fg(Color::Green).dimmed().paint("{")
262                )
263                .unwrap();
264            } else {
265                write!(current_buf, " ").unwrap();
266            }
267            self.print_kvs(&mut current_buf, data.kvs.iter().map(|(k, v)| (*k, v)))
268                .unwrap();
269            if self.config.bracketed_fields {
270                write!(
271                    current_buf,
272                    "{}",
273                    self.styled(Style::new().fg(Color::Green).bold(), "}") // Style::new().dimmed().paint("}")
274                )
275                .unwrap();
276            }
277        }
278
279        bufs.indent_current(indent, &self.config, style);
280        let writer = self.make_writer.make_writer();
281        bufs.flush_current_buf(writer)
282    }
283}
284
285impl<S, W> Layer<S> for HierarchicalLayer<W>
286where
287    S: Subscriber + for<'span> LookupSpan<'span>,
288    W: for<'writer> MakeWriter<'writer> + 'static,
289{
290    fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
291        let span = ctx.span(id).expect("in new_span but span does not exist");
292        if span.extensions().get::<Data>().is_none() {
293            let data = Data::new(attrs);
294            span.extensions_mut().insert(data);
295        }
296
297        if self.config.verbose_exit {
298            if let Some(span) = span.parent() {
299                self.write_span_info(&span.id(), &ctx, SpanMode::PreOpen);
300            }
301        }
302
303        self.write_span_info(
304            id,
305            &ctx,
306            SpanMode::Open {
307                verbose: self.config.verbose_entry,
308            },
309        );
310    }
311
312    fn on_event(&self, event: &Event<'_>, ctx: Context<S>) {
313        let mut guard = self.bufs.lock().unwrap();
314        let bufs = &mut *guard;
315        let mut event_buf = &mut bufs.current_buf;
316
317        // printing the indentation
318        let indent = ctx
319            .event_scope(event)
320            .map(|scope| scope.count())
321            .unwrap_or(0);
322
323        // check if this event occurred in the context of a span.
324        // if it has, get the start time of this span.
325        let start = match ctx.current_span().id() {
326            Some(id) => match ctx.span(id) {
327                // if the event is in a span, get the span's starting point.
328                Some(ctx) => {
329                    let ext = ctx.extensions();
330                    let data = ext
331                        .get::<Data>()
332                        .expect("Data cannot be found in extensions");
333                    Some(data.start)
334                }
335                None => None,
336            },
337            None => None,
338        };
339        if let Some(start) = start {
340            let elapsed = start.elapsed();
341            write!(
342                &mut event_buf,
343                "{timestamp}{unit} ",
344                timestamp = self.styled(Style::new().dimmed(), elapsed.as_millis().to_string()),
345                unit = self.styled(Style::new().dimmed(), "ms"),
346            )
347            .expect("Unable to write to buffer");
348        }
349
350        #[cfg(feature = "tracing-log")]
351        let normalized_meta = event.normalized_metadata();
352        #[cfg(feature = "tracing-log")]
353        let metadata = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
354        #[cfg(not(feature = "tracing-log"))]
355        let metadata = event.metadata();
356
357        if self.config.level {
358            let level = metadata.level();
359            let level = if self.config.ansi {
360                ColorLevel(level).to_string()
361            } else {
362                level.to_string()
363            };
364            write!(&mut event_buf, "{level}").expect("Unable to write to buffer");
365        }
366
367        if self.config.targets {
368            let target = metadata.target();
369            write!(
370                &mut event_buf,
371                " {}",
372                self.styled(Style::new().dimmed(), target,),
373            )
374            .expect("Unable to write to buffer");
375        }
376
377        let mut visitor = FmtEvent { comma: false, bufs };
378        event.record(&mut visitor);
379        visitor
380            .bufs
381            .indent_current(indent, &self.config, SpanMode::Event);
382        let writer = self.make_writer.make_writer();
383        bufs.flush_current_buf(writer)
384    }
385
386    fn on_close(&self, id: Id, ctx: Context<S>) {
387        self.write_span_info(
388            &id,
389            &ctx,
390            SpanMode::Close {
391                verbose: self.config.verbose_exit,
392            },
393        );
394
395        if self.config.verbose_exit {
396            if let Some(span) = ctx.span(&id).and_then(|span| span.parent()) {
397                self.write_span_info(&span.id(), &ctx, SpanMode::PostClose);
398            }
399        }
400    }
401}