1//! Define the ancestry of an event or span.
2//!
3//! See the documentation on the [`ExpectedAncestry`] enum for further details.
45use tracing_core::{
6 span::{self, Attributes},
7 Event,
8};
910use crate::span::{ActualSpan, ExpectedSpan};
1112/// The ancestry of an event or span.
13///
14/// An event or span can have an explicitly assigned parent, or be an explicit root. Otherwise,
15/// an event or span may have a contextually assigned parent or in the final case will be a
16/// contextual root.
17#[derive(Debug, Eq, PartialEq)]
18pub enum ExpectedAncestry {
19/// The event or span has an explicitly assigned parent (created with `parent: span_id`) span.
20HasExplicitParent(ExpectedSpan),
21/// The event or span is an explicitly defined root. It was created with `parent: None` and
22 /// has no parent.
23IsExplicitRoot,
24/// The event or span has a contextually assigned parent span. It has no explicitly assigned
25 /// parent span, nor has it been explicitly defined as a root (it was created without the
26 /// `parent:` directive). There was a span in context when this event or span was created.
27HasContextualParent(ExpectedSpan),
28/// The event or span is a contextual root. It has no explicitly assigned parent, nor has it
29 /// been explicitly defined as a root (it was created without the `parent:` directive).
30 /// Additionally, no span was in context when this event or span was created.
31IsContextualRoot,
32}
3334pub(crate) enum ActualAncestry {
35 HasExplicitParent(ActualSpan),
36 IsExplicitRoot,
37 HasContextualParent(ActualSpan),
38 IsContextualRoot,
39}
4041impl ExpectedAncestry {
42#[track_caller]
43pub(crate) fn check(
44&self,
45 actual_ancestry: &ActualAncestry,
46 ctx: impl std::fmt::Display,
47 collector_name: &str,
48 ) {
49match (self, actual_ancestry) {
50 (Self::IsExplicitRoot, ActualAncestry::IsExplicitRoot) => {}
51 (Self::IsContextualRoot, ActualAncestry::IsContextualRoot) => {}
52 (
53Self::HasExplicitParent(expected_parent),
54 ActualAncestry::HasExplicitParent(actual_parent),
55 ) => {
56 expected_parent.check(
57 actual_parent,
58format_args!("{ctx} to have an explicit parent span"),
59 collector_name,
60 );
61 }
62 (
63Self::HasContextualParent(expected_parent),
64 ActualAncestry::HasContextualParent(actual_parent),
65 ) => {
66println!("----> [{collector_name}] check {expected_parent:?} against actual parent with Id={id:?}", id = actual_parent.id());
67 expected_parent.check(
68 actual_parent,
69format_args!("{ctx} to have a contextual parent span"),
70 collector_name,
71 );
72 }
73_ => {
74// Ancestry types don't match at all.
75let expected_description = match self {
76Self::IsExplicitRoot => "be an explicit root",
77Self::HasExplicitParent(_) => "have an explicit parent span",
78Self::IsContextualRoot => "be a contextual root",
79Self::HasContextualParent(_) => "have a contextual parent span",
80 };
8182let actual_description = match actual_ancestry {
83 ActualAncestry::IsExplicitRoot => "is actually an explicit root",
84 ActualAncestry::HasExplicitParent(_) => "actually has an explicit parent span",
85 ActualAncestry::IsContextualRoot => "is actually a contextual root",
86 ActualAncestry::HasContextualParent(_) => {
87"actually has a contextual parent span"
88}
89 };
9091panic!(
92"{}",
93format!(
94"[{collector_name}] expected {ctx} to {expected_description}, \
95 but it {actual_description}"
96)
97 );
98 }
99 }
100 }
101}
102103pub(crate) trait HasAncestry {
104fn is_contextual(&self) -> bool;
105106fn is_root(&self) -> bool;
107108fn parent(&self) -> Option<&span::Id>;
109}
110111impl HasAncestry for &Event<'_> {
112fn is_contextual(&self) -> bool {
113 (self as &Event<'_>).is_contextual()
114 }
115116fn is_root(&self) -> bool {
117 (self as &Event<'_>).is_root()
118 }
119120fn parent(&self) -> Option<&span::Id> {
121 (self as &Event<'_>).parent()
122 }
123}
124125impl HasAncestry for &Attributes<'_> {
126fn is_contextual(&self) -> bool {
127 (self as &Attributes<'_>).is_contextual()
128 }
129130fn is_root(&self) -> bool {
131 (self as &Attributes<'_>).is_root()
132 }
133134fn parent(&self) -> Option<&span::Id> {
135 (self as &Attributes<'_>).parent()
136 }
137}
138139/// Determines the ancestry of an actual span or event.
140///
141/// The rules for determining the ancestry are as follows:
142///
143/// +------------+--------------+-----------------+---------------------+
144/// | Contextual | Current Span | Explicit Parent | Ancestry |
145/// +------------+--------------+-----------------+---------------------+
146/// | Yes | Yes | - | HasContextualParent |
147/// | Yes | No | - | IsContextualRoot |
148/// | No | - | Yes | HasExplicitParent |
149/// | No | - | No | IsExplicitRoot |
150/// +------------+--------------+-----------------+---------------------+
151pub(crate) fn get_ancestry(
152 item: impl HasAncestry,
153 lookup_current: impl FnOnce() -> Option<span::Id>,
154 actual_span: impl FnOnce(&span::Id) -> Option<ActualSpan>,
155) -> ActualAncestry {
156if item.is_contextual() {
157if let Some(parent_id) = lookup_current() {
158let contextual_parent_span = actual_span(&parent_id).expect(
159"tracing-mock: contextual parent cannot \
160 be looked up by ID. Was it recorded correctly?",
161 );
162 ActualAncestry::HasContextualParent(contextual_parent_span)
163 } else {
164 ActualAncestry::IsContextualRoot
165 }
166 } else if item.is_root() {
167 ActualAncestry::IsExplicitRoot
168 } else {
169let parent_id = item.parent().expect(
170"tracing-mock: is_contextual=false is_root=false \
171 but no explicit parent found. This is a bug!",
172 );
173let explicit_parent_span = actual_span(parent_id).expect(
174"tracing-mock: explicit parent cannot be looked \
175 up by ID. Is the provided Span ID valid: {parent_id}",
176 );
177 ActualAncestry::HasExplicitParent(explicit_parent_span)
178 }
179}