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}