tracing_forest/layer/
mod.rs

1use crate::fail;
2use crate::printer::{PrettyPrinter, TestCapturePrinter};
3use crate::processor::{Processor, Sink};
4use crate::tag::{NoTag, Tag, TagParser};
5use crate::tree::{self, FieldSet, Tree};
6#[cfg(feature = "chrono")]
7use chrono::Utc;
8use std::fmt;
9use std::io::{self, Write};
10use std::time::Instant;
11use tracing::field::{Field, Visit};
12use tracing::span::{Attributes, Id, Record};
13use tracing::{Event, Subscriber};
14use tracing_subscriber::layer::{Context, Layer, SubscriberExt};
15use tracing_subscriber::registry::{LookupSpan, Registry, SpanRef};
16use tracing_subscriber::util::SubscriberInitExt;
17use tracing_subscriber::util::TryInitError;
18#[cfg(feature = "uuid")]
19use uuid::Uuid;
20#[cfg(feature = "uuid")]
21pub(crate) mod id;
22
23pub(crate) struct OpenedSpan {
24    span: tree::Span,
25    start: Instant,
26}
27
28impl OpenedSpan {
29    fn new<S>(attrs: &Attributes, _ctx: &Context<S>) -> Self
30    where
31        S: Subscriber + for<'a> LookupSpan<'a>,
32    {
33        let mut fields = FieldSet::default();
34        #[cfg(feature = "uuid")]
35        let mut maybe_uuid = None;
36        #[cfg(feature = "uuid")]
37        let mut defer = false;
38
39        attrs.record(&mut |field: &Field, value: &dyn fmt::Debug| {
40            #[cfg(feature = "uuid")]
41            if field.name() == "uuid" && maybe_uuid.is_none() {
42                const LENGTH: usize = 45;
43                let mut buf = [0u8; LENGTH];
44                let mut remaining = &mut buf[..];
45
46                if let Ok(()) = write!(remaining, "{value:?}") {
47                    let len = LENGTH - remaining.len();
48                    if let Some(parsed) = id::try_parse(&buf[..len]) {
49                        maybe_uuid = Some(parsed);
50                    }
51                }
52                return;
53            }
54
55            #[cfg(feature = "defer")]
56            if field.name() == "defer" {
57                defer = true;
58                return;
59            }
60
61            let value = format!("{value:?}");
62            fields.push(tree::Field::new(field.name(), value));
63        });
64
65        let shared = tree::Shared {
66            #[cfg(feature = "chrono")]
67            timestamp: Utc::now(),
68            level: *attrs.metadata().level(),
69            fields,
70            #[cfg(feature = "uuid")]
71            uuid: maybe_uuid.unwrap_or_else(|| match _ctx.lookup_current() {
72                Some(parent) => parent
73                    .extensions()
74                    .get::<OpenedSpan>()
75                    .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
76                    .span
77                    .uuid(),
78                None => Uuid::new_v4(),
79            }),
80        };
81
82        let span = tree::Span::new(shared, attrs.metadata().name());
83
84        #[cfg(feature = "defer")]
85        let span = span.defer_unless_children_attached(defer);
86
87        OpenedSpan {
88            span,
89            start: Instant::now(),
90        }
91    }
92
93    fn enter(&mut self) {
94        self.start = Instant::now();
95    }
96
97    fn exit(&mut self) {
98        self.span.total_duration += self.start.elapsed();
99    }
100
101    fn close(self) -> tree::Span {
102        self.span
103    }
104
105    fn record_event(&mut self, event: tree::Event) {
106        #[cfg(feature = "uuid")]
107        let event = {
108            let mut event = event;
109            event.shared.uuid = self.span.uuid();
110            event
111        };
112
113        self.span.nodes.push(Tree::Event(event));
114    }
115
116    fn record_span(&mut self, span: tree::Span) {
117        self.span.inner_duration += span.total_duration();
118        self.span.nodes.push(Tree::Span(span));
119    }
120
121    #[cfg(feature = "uuid")]
122    pub(crate) fn uuid(&self) -> Uuid {
123        self.span.uuid()
124    }
125}
126
127/// A [`Layer`] that collects and processes trace data while preserving
128/// contextual coherence.
129#[derive(Clone, Debug)]
130pub struct ForestLayer<P, T> {
131    processor: P,
132    tag: T,
133}
134
135impl<P: Processor, T: TagParser> ForestLayer<P, T> {
136    /// Create a new `ForestLayer` from a [`Processor`] and a [`TagParser`].
137    pub fn new(processor: P, tag: T) -> Self {
138        ForestLayer { processor, tag }
139    }
140}
141
142impl<P: Processor> From<P> for ForestLayer<P, NoTag> {
143    fn from(processor: P) -> Self {
144        ForestLayer::new(processor, NoTag)
145    }
146}
147
148impl ForestLayer<Sink, NoTag> {
149    /// Create a new `ForestLayer` that does nothing with collected trace data.
150    pub fn sink() -> Self {
151        ForestLayer::from(Sink)
152    }
153}
154
155impl Default for ForestLayer<PrettyPrinter, NoTag> {
156    fn default() -> Self {
157        ForestLayer {
158            processor: PrettyPrinter::new(),
159            tag: NoTag,
160        }
161    }
162}
163
164impl<P, T, S> Layer<S> for ForestLayer<P, T>
165where
166    P: Processor,
167    T: TagParser,
168    S: Subscriber + for<'a> LookupSpan<'a>,
169{
170    fn on_new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
171        let span = ctx.span(id).expect(fail::SPAN_NOT_IN_CONTEXT);
172        let opened = OpenedSpan::new(attrs, &ctx);
173
174        if !ctx.enabled(attrs.metadata()) {
175            return;
176        }
177
178        let mut extensions = span.extensions_mut();
179        extensions.insert(opened);
180    }
181
182    fn on_record(&self, span: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
183        struct Visitor<'a> {
184            fields: &'a mut FieldSet,
185        }
186
187        impl<'a> Visit for Visitor<'a> {
188            fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
189                let value = format!("{:?}", value);
190                let new_field = tree::Field::new(field.name(), value);
191
192                if let Some(old_field) = self
193                    .fields
194                    .iter_mut()
195                    .find(|old_field| old_field.key() == field.name())
196                {
197                    *old_field = new_field;
198                } else {
199                    self.fields.push(new_field);
200                }
201            }
202        }
203
204        let span = ctx.span(span).expect(fail::SPAN_NOT_IN_CONTEXT);
205        let mut extensions = span.extensions_mut();
206        let openedspan = extensions
207            .get_mut::<OpenedSpan>()
208            .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS);
209
210        let mut visitor = Visitor {
211            fields: &mut openedspan.span.shared.fields,
212        };
213        values.record(&mut visitor);
214    }
215
216    fn on_event(&self, event: &Event, ctx: Context<S>) {
217        struct Visitor {
218            message: Option<String>,
219            fields: FieldSet,
220            immediate: bool,
221        }
222
223        impl Visit for Visitor {
224            fn record_bool(&mut self, field: &Field, value: bool) {
225                match field.name() {
226                    "immediate" => self.immediate |= value,
227                    _ => self.record_debug(field, &value),
228                }
229            }
230
231            fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
232                let value = format!("{value:?}");
233                match field.name() {
234                    "message" if self.message.is_none() => self.message = Some(value),
235                    key => self.fields.push(tree::Field::new(key, value)),
236                }
237            }
238        }
239
240        let mut visitor = Visitor {
241            message: None,
242            fields: FieldSet::default(),
243            immediate: false,
244        };
245
246        event.record(&mut visitor);
247
248        let shared = tree::Shared {
249            #[cfg(feature = "uuid")]
250            uuid: Uuid::nil(),
251            #[cfg(feature = "chrono")]
252            timestamp: Utc::now(),
253            level: *event.metadata().level(),
254            fields: visitor.fields,
255        };
256
257        let tree_event = tree::Event {
258            shared,
259            message: visitor.message,
260            tag: self.tag.parse(event),
261        };
262
263        let current_span = ctx.event_span(event);
264
265        if visitor.immediate {
266            write_immediate(&tree_event, current_span.as_ref()).expect("writing urgent failed");
267        }
268
269        match current_span.as_ref() {
270            Some(parent) => parent
271                .extensions_mut()
272                .get_mut::<OpenedSpan>()
273                .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
274                .record_event(tree_event),
275            None => self
276                .processor
277                .process(Tree::Event(tree_event))
278                .expect(fail::PROCESSING_ERROR),
279        }
280    }
281
282    fn on_enter(&self, id: &Id, ctx: Context<S>) {
283        ctx.span(id)
284            .expect(fail::SPAN_NOT_IN_CONTEXT)
285            .extensions_mut()
286            .get_mut::<OpenedSpan>()
287            .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
288            .enter();
289    }
290
291    fn on_exit(&self, id: &Id, ctx: Context<S>) {
292        ctx.span(id)
293            .expect(fail::SPAN_NOT_IN_CONTEXT)
294            .extensions_mut()
295            .get_mut::<OpenedSpan>()
296            .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
297            .exit();
298    }
299
300    fn on_close(&self, id: Id, ctx: Context<S>) {
301        let span_ref = ctx.span(&id).expect(fail::SPAN_NOT_IN_CONTEXT);
302
303        let mut span = span_ref
304            .extensions_mut()
305            .remove::<OpenedSpan>()
306            .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
307            .close();
308
309        // Ensure that the total duration is at least as much as the inner
310        // duration. This is caused by when a child span is manually passed
311        // a parent span and then enters without entering the parent span. Also
312        // when a child span is created within a parent, and then stored and
313        // entered again when the parent isn't opened.
314        //
315        // Issue: https://github.com/QnnOkabayashi/tracing-forest/issues/11
316        if span.total_duration < span.inner_duration {
317            span.total_duration = span.inner_duration;
318        }
319
320        match span_ref.parent() {
321            Some(parent) => parent
322                .extensions_mut()
323                .get_mut::<OpenedSpan>()
324                .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
325                .record_span(span),
326            None => self
327                .processor
328                .process(Tree::Span(span))
329                .expect(fail::PROCESSING_ERROR),
330        }
331    }
332}
333
334fn write_immediate<S>(event: &tree::Event, current: Option<&SpanRef<S>>) -> io::Result<()>
335where
336    S: for<'a> LookupSpan<'a>,
337{
338    // uuid timestamp LEVEL root > inner > leaf > my message here | key: val
339    #[cfg(feature = "smallvec")]
340    let mut writer = smallvec::SmallVec::<[u8; 256]>::new();
341    #[cfg(not(feature = "smallvec"))]
342    let mut writer = Vec::with_capacity(256);
343
344    #[cfg(feature = "uuid")]
345    if let Some(span) = current {
346        let uuid = span
347            .extensions()
348            .get::<OpenedSpan>()
349            .expect(fail::OPENED_SPAN_NOT_IN_EXTENSIONS)
350            .span
351            .uuid();
352        write!(writer, "{uuid} ")?;
353    }
354
355    #[cfg(feature = "chrono")]
356    write!(writer, "{} ", event.timestamp().to_rfc3339())?;
357
358    write!(writer, "{:<8} ", event.level())?;
359
360    let tag = event.tag().unwrap_or_else(|| Tag::from(event.level()));
361
362    write!(writer, "{icon} IMMEDIATE {icon} ", icon = tag.icon())?;
363
364    if let Some(span) = current {
365        for ancestor in span.scope().from_root() {
366            write!(writer, "{} > ", ancestor.name())?;
367        }
368    }
369
370    // we should just do pretty printing here.
371
372    if let Some(message) = event.message() {
373        write!(writer, "{message}")?;
374    }
375
376    for field in event.fields() {
377        write!(writer, " | {}: {}", field.key(), field.value())?;
378    }
379
380    writeln!(writer)?;
381
382    io::stderr().write_all(&writer)
383}
384
385/// Initializes a global subscriber with a [`ForestLayer`] using the default configuration.
386///
387/// This function is intended for quick initialization and processes log trees "inline",
388/// meaning it doesn't take advantage of a worker task for formatting and writing.
389/// To use a worker task, consider using the [`worker_task`] function. Alternatively,
390/// configure a `Subscriber` manually using a `ForestLayer`.
391///
392/// [`worker_task`]: crate::runtime::worker_task
393///
394/// # Examples
395/// ```
396/// use tracing::{info, info_span};
397///
398/// tracing_forest::init();
399///
400/// info!("Hello, world!");
401/// info_span!("my_span").in_scope(|| {
402///     info!("Relevant information");
403/// });
404/// ```
405/// Produces the the output:
406/// ```log
407/// INFO     i [info]: Hello, world!
408/// INFO     my_span [ 26.0µs | 100.000% ]
409/// INFO     ┕━ i [info]: Relevant information
410/// ```
411pub fn init() {
412    Registry::default().with(ForestLayer::default()).init();
413}
414
415/// Initializes a global subscriber for cargo tests with a [`ForestLayer`] using the default
416/// configuration.
417///
418/// This function is intended for test case initialization and processes log trees "inline",
419/// meaning it doesn't take advantage of a worker task for formatting and writing.
420///
421/// [`worker_task`]: crate::runtime::worker_task
422///
423/// # Examples
424/// ```
425/// use tracing::{info, info_span};
426///
427/// let _ = tracing_forest::test_init();
428///
429/// info!("Hello, world!");
430/// info_span!("my_span").in_scope(|| {
431///     info!("Relevant information");
432/// });
433/// ```
434pub fn test_init() -> Result<(), TryInitError> {
435    Registry::default()
436        .with(ForestLayer::new(TestCapturePrinter::new(), NoTag))
437        .try_init()
438}