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
89#[derive(Clone, Debug)]
90#[cfg_attr(feature = "serde", derive(Serialize))]
91pub(crate) struct Shared {
92    /// The ID of the event or span.
93    #[cfg(feature = "uuid")]
94    pub(crate) uuid: Uuid,
95
96    /// When the event occurred or when the span opened.
97    #[cfg(feature = "chrono")]
98    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::timestamp"))]
99    pub(crate) timestamp: DateTime<Utc>,
100
101    /// The level the event or span occurred at.
102    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::level"))]
103    pub(crate) level: Level,
104
105    /// Key-value data.
106    #[cfg_attr(feature = "serde", serde(serialize_with = "ser::fields"))]
107    pub(crate) fields: FieldSet,
108}
109
110/// Error returned by [`Tree::event`][event].
111///
112/// [event]: crate::tree::Tree::event
113#[derive(Error, Debug)]
114#[error("Expected an event, found a span")]
115pub struct ExpectedEventError(());
116
117/// Error returned by [`Tree::span`][span].
118///
119/// [span]: crate::tree::Tree::span
120#[derive(Error, Debug)]
121#[error("Expected a span, found an event")]
122pub struct ExpectedSpanError(());
123
124impl Tree {
125    /// Returns a reference to the inner [`Event`] if the tree is an event.
126    ///
127    /// # Errors
128    ///
129    /// This function returns an error if the `Tree` contains the `Span` variant.
130    ///
131    /// # Examples
132    ///
133    /// Inspecting a `Tree` returned from [`capture`]:
134    /// ```
135    /// use tracing::{info, info_span};
136    /// use tracing_forest::tree::{Tree, Event};
137    ///
138    /// #[tokio::main]
139    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
140    ///     let logs: Vec<Tree> = tracing_forest::capture()
141    ///         .build()
142    ///         .on(async {
143    ///             info!("some information");
144    ///         })
145    ///         .await;
146    ///
147    ///     assert!(logs.len() == 1);
148    ///
149    ///     let event: &Event = logs[0].event()?;
150    ///     assert!(event.message() == Some("some information"));
151    ///
152    ///     Ok(())
153    /// }
154    /// ```
155    ///
156    /// [`capture`]: crate::runtime::capture
157    pub fn event(&self) -> Result<&Event, ExpectedEventError> {
158        match self {
159            Tree::Event(event) => Ok(event),
160            Tree::Span(_) => Err(ExpectedEventError(())),
161        }
162    }
163
164    /// Returns a reference to the inner [`Span`] if the tree is a span.
165    ///
166    /// # Errors
167    ///
168    /// This function returns an error if the `Tree` contains the `Event` variant.
169    ///
170    /// # Examples
171    ///
172    /// Inspecting a `Tree` returned from [`capture`]:
173    /// ```
174    /// use tracing::{info, info_span};
175    /// use tracing_forest::tree::{Tree, Span};
176    ///
177    /// #[tokio::main]
178    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
179    ///     let logs: Vec<Tree> = tracing_forest::capture()
180    ///         .build()
181    ///         .on(async {
182    ///             info_span!("my_span").in_scope(|| {
183    ///                 info!("inside the span");
184    ///             });
185    ///         })
186    ///         .await;
187    ///
188    ///     assert!(logs.len() == 1);
189    ///
190    ///     let my_span: &Span = logs[0].span()?;
191    ///     assert!(my_span.name() == "my_span");
192    ///     Ok(())
193    /// }
194    /// ```
195    ///
196    /// [`capture`]: crate::runtime::capture
197    pub fn span(&self) -> Result<&Span, ExpectedSpanError> {
198        match self {
199            Tree::Event(_) => Err(ExpectedSpanError(())),
200            Tree::Span(span) => Ok(span),
201        }
202    }
203}
204
205impl Event {
206    /// Returns the event's [`Uuid`].
207    #[cfg(feature = "uuid")]
208    pub fn uuid(&self) -> Uuid {
209        self.shared.uuid
210    }
211
212    /// Returns the [`DateTime`] that the event occurred at.
213    #[cfg(feature = "chrono")]
214    pub fn timestamp(&self) -> DateTime<Utc> {
215        self.shared.timestamp
216    }
217
218    /// Returns the event's [`Level`].
219    pub fn level(&self) -> Level {
220        self.shared.level
221    }
222
223    /// Returns the event's message, if there is one.
224    pub fn message(&self) -> Option<&str> {
225        self.message.as_deref()
226    }
227
228    /// Returns the event's [`Tag`], if there is one.
229    pub fn tag(&self) -> Option<Tag> {
230        self.tag
231    }
232
233    /// Returns the event's fields.
234    pub fn fields(&self) -> &[Field] {
235        &self.shared.fields
236    }
237}
238
239impl Span {
240    pub(crate) fn new(shared: Shared, name: &'static str) -> Self {
241        Span {
242            shared,
243            name,
244            total_duration: Duration::ZERO,
245            inner_duration: Duration::ZERO,
246            nodes: Vec::new(),
247        }
248    }
249
250    /// Returns the span's [`Uuid`].
251    #[cfg(feature = "uuid")]
252    pub fn uuid(&self) -> Uuid {
253        self.shared.uuid
254    }
255
256    /// Returns the [`DateTime`] that the span occurred at.
257    #[cfg(feature = "chrono")]
258    pub fn timestamp(&self) -> DateTime<Utc> {
259        self.shared.timestamp
260    }
261
262    /// Returns the span's [`Level`].
263    pub fn level(&self) -> Level {
264        self.shared.level
265    }
266
267    /// Returns the span's name.
268    pub fn name(&self) -> &str {
269        self.name
270    }
271
272    /// Returns the span's child trees.
273    pub fn nodes(&self) -> &[Tree] {
274        &self.nodes
275    }
276
277    /// Returns the total duration the span was entered for.
278    ///
279    /// If the span was used to instrument a `Future`, this only accounts for the
280    /// time spent polling the `Future`. For example, time spent sleeping will
281    /// not be accounted for.
282    pub fn total_duration(&self) -> Duration {
283        self.total_duration
284    }
285
286    /// Returns the duration that inner spans were opened for.
287    pub fn inner_duration(&self) -> Duration {
288        self.inner_duration
289    }
290
291    /// Returns the duration this span was entered, but not in any child spans.
292    pub fn base_duration(&self) -> Duration {
293        self.total_duration - self.inner_duration
294    }
295}