🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_mock/
ancestry.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Define the ancestry of an event or span.
//!
//! See the documentation on the [`ExpectedAncestry`] enum for further details.

use tracing_core::{
    span::{self, Attributes},
    Event,
};

use crate::span::{ActualSpan, ExpectedSpan};

/// The ancestry of an event or span.
///
/// An event or span can have an explicitly assigned parent, or be an explicit root. Otherwise,
/// an event or span may have a contextually assigned parent or in the final case will be a
/// contextual root.
#[derive(Debug, Eq, PartialEq)]
pub enum ExpectedAncestry {
    /// The event or span has an explicitly assigned parent (created with `parent: span_id`) span.
    HasExplicitParent(ExpectedSpan),
    /// The event or span is an explicitly defined root. It was created with `parent: None` and
    /// has no parent.
    IsExplicitRoot,
    /// The event or span has a contextually assigned parent span. It has no explicitly assigned
    /// parent span, nor has it been explicitly defined as a root (it was created without the
    /// `parent:` directive). There was a span in context when this event or span was created.
    HasContextualParent(ExpectedSpan),
    /// The event or span is a contextual root. It has no explicitly assigned parent, nor has it
    /// been explicitly defined as a root (it was created without the `parent:` directive).
    /// Additionally, no span was in context when this event or span was created.
    IsContextualRoot,
}

pub(crate) enum ActualAncestry {
    HasExplicitParent(ActualSpan),
    IsExplicitRoot,
    HasContextualParent(ActualSpan),
    IsContextualRoot,
}

impl ExpectedAncestry {
    #[track_caller]
    pub(crate) fn check(
        &self,
        actual_ancestry: &ActualAncestry,
        ctx: impl std::fmt::Display,
        collector_name: &str,
    ) {
        match (self, actual_ancestry) {
            (Self::IsExplicitRoot, ActualAncestry::IsExplicitRoot) => {}
            (Self::IsContextualRoot, ActualAncestry::IsContextualRoot) => {}
            (
                Self::HasExplicitParent(expected_parent),
                ActualAncestry::HasExplicitParent(actual_parent),
            ) => {
                expected_parent.check(
                    actual_parent,
                    format_args!("{ctx} to have an explicit parent span"),
                    collector_name,
                );
            }
            (
                Self::HasContextualParent(expected_parent),
                ActualAncestry::HasContextualParent(actual_parent),
            ) => {
                println!("----> [{collector_name}] check {expected_parent:?} against actual parent with Id={id:?}", id = actual_parent.id());
                expected_parent.check(
                    actual_parent,
                    format_args!("{ctx} to have a contextual parent span"),
                    collector_name,
                );
            }
            _ => {
                // Ancestry types don't match at all.
                let expected_description = match self {
                    Self::IsExplicitRoot => "be an explicit root",
                    Self::HasExplicitParent(_) => "have an explicit parent span",
                    Self::IsContextualRoot => "be a contextual root",
                    Self::HasContextualParent(_) => "have a contextual parent span",
                };

                let actual_description = match actual_ancestry {
                    ActualAncestry::IsExplicitRoot => "is actually an explicit root",
                    ActualAncestry::HasExplicitParent(_) => "actually has an explicit parent span",
                    ActualAncestry::IsContextualRoot => "is actually a contextual root",
                    ActualAncestry::HasContextualParent(_) => {
                        "actually has a contextual parent span"
                    }
                };

                panic!(
                    "{}",
                    format!(
                        "[{collector_name}] expected {ctx} to {expected_description}, \
                        but it {actual_description}"
                    )
                );
            }
        }
    }
}

pub(crate) trait HasAncestry {
    fn is_contextual(&self) -> bool;

    fn is_root(&self) -> bool;

    fn parent(&self) -> Option<&span::Id>;
}

impl HasAncestry for &Event<'_> {
    fn is_contextual(&self) -> bool {
        (self as &Event<'_>).is_contextual()
    }

    fn is_root(&self) -> bool {
        (self as &Event<'_>).is_root()
    }

    fn parent(&self) -> Option<&span::Id> {
        (self as &Event<'_>).parent()
    }
}

impl HasAncestry for &Attributes<'_> {
    fn is_contextual(&self) -> bool {
        (self as &Attributes<'_>).is_contextual()
    }

    fn is_root(&self) -> bool {
        (self as &Attributes<'_>).is_root()
    }

    fn parent(&self) -> Option<&span::Id> {
        (self as &Attributes<'_>).parent()
    }
}

/// Determines the ancestry of an actual span or event.
///
/// The rules for determining the ancestry are as follows:
///
/// +------------+--------------+-----------------+---------------------+
/// | Contextual | Current Span | Explicit Parent | Ancestry            |
/// +------------+--------------+-----------------+---------------------+
/// | Yes        | Yes          | -               | HasContextualParent |
/// | Yes        | No           | -               | IsContextualRoot    |
/// | No         | -            | Yes             | HasExplicitParent   |
/// | No         | -            | No              | IsExplicitRoot      |
/// +------------+--------------+-----------------+---------------------+
pub(crate) fn get_ancestry(
    item: impl HasAncestry,
    lookup_current: impl FnOnce() -> Option<span::Id>,
    actual_span: impl FnOnce(&span::Id) -> Option<ActualSpan>,
) -> ActualAncestry {
    if item.is_contextual() {
        if let Some(parent_id) = lookup_current() {
            let contextual_parent_span = actual_span(&parent_id).expect(
                "tracing-mock: contextual parent cannot \
                            be looked up by ID. Was it recorded correctly?",
            );
            ActualAncestry::HasContextualParent(contextual_parent_span)
        } else {
            ActualAncestry::IsContextualRoot
        }
    } else if item.is_root() {
        ActualAncestry::IsExplicitRoot
    } else {
        let parent_id = item.parent().expect(
            "tracing-mock: is_contextual=false is_root=false \
                        but no explicit parent found. This is a bug!",
        );
        let explicit_parent_span = actual_span(parent_id).expect(
            "tracing-mock: explicit parent cannot be looked \
                        up by ID. Is the provided Span ID valid: {parent_id}",
        );
        ActualAncestry::HasExplicitParent(explicit_parent_span)
    }
}