tracing_forest/tree/
mod.rs

1//! The core tree structure of `tracing-forest`.
2//!
3//! This module provides methods used for log inspection when using [`capture`].
4//! It consists of three types: [`Tree`], [`Span`], and [`Event`].
5//!
6//! [`capture`]: crate::runtime::capture
7use crate::tag::Tag;
8#[cfg(feature = "chrono")]
9use chrono::{DateTime, Utc};
10#[cfg(feature = "serde")]
11use serde::Serialize;
12use std::time::Duration;
13use thiserror::Error;
14use tracing::Level;
15#[cfg(feature = "uuid")]
16use uuid::Uuid;
17
18mod field;
19#[cfg(feature = "serde")]
20mod ser;
21
22pub use field::Field;
23pub(crate) use field::FieldSet;
24
25/// A node in the log tree, consisting of either a [`Span`] or an [`Event`].
26///
27/// The inner types can be extracted through a `match` statement. Alternatively,
28/// the [`event`] and [`span`] methods provide a more ergonomic way to access the
29/// inner types in unit tests when combined with the [`capture`] function.
30///
31/// [`event`]: Tree::event
32/// [`span`]: Tree::span
33/// [`capture`]: crate::runtime::capture
34#[derive(Clone, Debug)]
35#[cfg_attr(feature = "serde", derive(Serialize))]
36#[allow(clippy::large_enum_variant)] // https://github.com/rust-lang/rust-clippy/issues/9798
37pub enum Tree {
38    /// An [`Event`] leaf node.
39    Event(Event),
40
41    /// A [`Span`] inner node.
42    Span(Span),
43}
44
45/// A leaf node in the log tree carrying information about a Tracing event.
46#[derive(Clone, Debug)]
47#[cfg_attr(feature = "serde", derive(Serialize))]
48pub struct Event {
49    /// Shared fields between events and spans.
50    #[cfg_attr(feature = "serde", serde(flatten))]
51    pub(crate) shared: Shared,
52
53    /// The message associated with the event.
54    pub(crate) message: Option<String>,
55
56    /// The tag that the event was collected with.
57    pub(crate) tag: Option<Tag>,
58}
59
60/// An internal node in the log tree carrying information about a Tracing span.
61#[derive(Clone, Debug)]
62#[cfg_attr(feature = "serde", derive(Serialize))]
63pub struct Span {
64    /// Shared fields between events and spans.
65    #[cfg_attr(feature = "serde", serde(flatten))]
66    pub(crate) shared: Shared,
67
68    /// The name of the span.
69    pub(crate) name: &'static str,
70
71    /// The total duration the span was open for.
72    #[cfg_attr(
73        feature = "serde",
74        serde(rename = "nanos_total", serialize_with = "ser::nanos")
75    )]
76    pub(crate) total_duration: Duration,
77
78    /// The total duration inner spans were open for.
79    #[cfg_attr(
80        feature = "serde",
81        serde(rename = "nanos_nested", serialize_with = "ser::nanos")
82    )]
83    pub(crate) inner_duration: Duration,
84
85    /// Events and spans collected while the span was open.
86    pub(crate) nodes: Vec<Tree>,
87
88    /// This span is only displayed *if* there are child nodes in the tree. Else it
89    /// will NOT be rendered.
90    #[cfg(feature = "defer")]
91    pub(crate) defer_unless_children_attached: bool,
92}
93
94#[derive(Clone, Debug)]
95#[cfg_attr(feature = "serde", derive(Serialize))]
96pub(crate) struct Shared {
97    /// The ID of the event or span.
98    #[cfg(feature = "uuid")]
99    pub(crate) uuid: Uuid,
100
101    /// When the event occurred or when the span opened.
102    #[cfg(feature = "chrono")]
103    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::timestamp"))]
104    pub(crate) timestamp: DateTime<Utc>,
105
106    /// The level the event or span occurred at.
107    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::level"))]
108    pub(crate) level: Level,
109
110    /// Key-value data.
111    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::fields"))]
112    pub(crate) fields: FieldSet,
113}
114
115/// Error returned by [`Tree::event`][event].
116///
117/// [event]: crate::tree::Tree::event
118#[derive(Error, Debug)]
119#[error("Expected an event, found a span")]
120pub struct ExpectedEventError(());
121
122/// Error returned by [`Tree::span`][span].
123///
124/// [span]: crate::tree::Tree::span
125#[derive(Error, Debug)]
126#[error("Expected a span, found an event")]
127pub struct ExpectedSpanError(());
128
129impl Tree {
130    /// Returns a reference to the inner [`Event`] if the tree is an event.
131    ///
132    /// # Errors
133    ///
134    /// This function returns an error if the `Tree` contains the `Span` variant.
135    ///
136    /// # Examples
137    ///
138    /// Inspecting a `Tree` returned from [`capture`]:
139    /// ```
140    /// use tracing::{info, info_span};
141    /// use tracing_forest::tree::{Tree, Event};
142    ///
143    /// #[tokio::main]
144    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
145    ///     let logs: Vec<Tree> = tracing_forest::capture()
146    ///         .build()
147    ///         .on(async {
148    ///             info!("some information");
149    ///         })
150    ///         .await;
151    ///
152    ///     assert!(logs.len() == 1);
153    ///
154    ///     let event: &Event = logs[0].event()?;
155    ///     assert!(event.message() == Some("some information"));
156    ///
157    ///     Ok(())
158    /// }
159    /// ```
160    ///
161    /// [`capture`]: crate::runtime::capture
162    pub fn event(&self) -> Result<&Event, ExpectedEventError> {
163        match self {
164            Tree::Event(event) => Ok(event),
165            Tree::Span(_) => Err(ExpectedEventError(())),
166        }
167    }
168
169    /// Returns a reference to the inner [`Span`] if the tree is a span.
170    ///
171    /// # Errors
172    ///
173    /// This function returns an error if the `Tree` contains the `Event` variant.
174    ///
175    /// # Examples
176    ///
177    /// Inspecting a `Tree` returned from [`capture`]:
178    /// ```
179    /// use tracing::{info, info_span};
180    /// use tracing_forest::tree::{Tree, Span};
181    ///
182    /// #[tokio::main]
183    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
184    ///     let logs: Vec<Tree> = tracing_forest::capture()
185    ///         .build()
186    ///         .on(async {
187    ///             info_span!("my_span").in_scope(|| {
188    ///                 info!("inside the span");
189    ///             });
190    ///         })
191    ///         .await;
192    ///
193    ///     assert!(logs.len() == 1);
194    ///
195    ///     let my_span: &Span = logs[0].span()?;
196    ///     assert!(my_span.name() == "my_span");
197    ///     Ok(())
198    /// }
199    /// ```
200    ///
201    /// [`capture`]: crate::runtime::capture
202    pub fn span(&self) -> Result<&Span, ExpectedSpanError> {
203        match self {
204            Tree::Event(_) => Err(ExpectedSpanError(())),
205            Tree::Span(span) => Ok(span),
206        }
207    }
208
209    pub(crate) fn should_render(&self) -> bool {
210        match self {
211            Tree::Event(_) => true,
212            Tree::Span(span) => span.should_render(),
213        }
214    }
215}
216
217impl Event {
218    /// Returns the event's [`Uuid`].
219    #[cfg(feature = "uuid")]
220    pub fn uuid(&self) -> Uuid {
221        self.shared.uuid
222    }
223
224    /// Returns the [`DateTime`] that the event occurred at.
225    #[cfg(feature = "chrono")]
226    pub fn timestamp(&self) -> DateTime<Utc> {
227        self.shared.timestamp
228    }
229
230    /// Returns the event's [`Level`].
231    pub fn level(&self) -> Level {
232        self.shared.level
233    }
234
235    /// Returns the event's message, if there is one.
236    pub fn message(&self) -> Option<&str> {
237        self.message.as_deref()
238    }
239
240    /// Returns the event's [`Tag`], if there is one.
241    pub fn tag(&self) -> Option<Tag> {
242        self.tag
243    }
244
245    /// Returns the event's fields.
246    pub fn fields(&self) -> &[Field] {
247        &self.shared.fields
248    }
249}
250
251impl Span {
252    pub(crate) fn new(shared: Shared, name: &'static str) -> Self {
253        Span {
254            shared,
255            name,
256            total_duration: Duration::ZERO,
257            inner_duration: Duration::ZERO,
258            nodes: Vec::new(),
259            #[cfg(feature = "defer")]
260            defer_unless_children_attached: false,
261        }
262    }
263
264    #[cfg(feature = "defer")]
265    pub(crate) fn defer_unless_children_attached(mut self, defer: bool) -> Self {
266        self.defer_unless_children_attached = defer;
267        self
268    }
269
270    #[cfg(feature = "defer")]
271    pub(crate) fn should_render(&self) -> bool {
272        !(self.defer_unless_children_attached && self.nodes().is_empty())
273    }
274
275    #[cfg(not(feature = "defer"))]
276    pub(crate) fn should_render(&self) -> bool {
277        true
278    }
279
280    /// Returns the span's [`Uuid`].
281    #[cfg(feature = "uuid")]
282    pub fn uuid(&self) -> Uuid {
283        self.shared.uuid
284    }
285
286    /// Returns the [`DateTime`] that the span occurred at.
287    #[cfg(feature = "chrono")]
288    pub fn timestamp(&self) -> DateTime<Utc> {
289        self.shared.timestamp
290    }
291
292    /// Returns the span's [`Level`].
293    pub fn level(&self) -> Level {
294        self.shared.level
295    }
296
297    /// Returns the span's name.
298    pub fn name(&self) -> &str {
299        self.name
300    }
301
302    /// Returns the span's fields.
303    pub fn fields(&self) -> &[Field] {
304        &self.shared.fields
305    }
306
307    /// Returns the span's child trees.
308    pub fn nodes(&self) -> &[Tree] {
309        &self.nodes
310    }
311
312    /// Returns the total duration the span was entered for.
313    ///
314    /// If the span was used to instrument a `Future`, this only accounts for the
315    /// time spent polling the `Future`. For example, time spent sleeping will
316    /// not be accounted for.
317    pub fn total_duration(&self) -> Duration {
318        self.total_duration
319    }
320
321    /// Returns the duration that inner spans were opened for.
322    pub fn inner_duration(&self) -> Duration {
323        self.inner_duration
324    }
325
326    /// Returns the duration this span was entered, but not in any child spans.
327    pub fn base_duration(&self) -> Duration {
328        self.total_duration - self.inner_duration
329    }
330}