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}