relay_event_normalization/normalize/span/
reparent_broken_spans.rs1use std::collections::BTreeSet;
4use std::mem;
5
6use relay_event_schema::protocol::{Event, TraceContext};
7use relay_protocol::Error;
8
9pub fn reparent_broken_spans(event: &mut Event) {
15 let Some(spans) = event.spans.value_mut() else {
16 return;
17 };
18
19 let Some(contexts) = event.contexts.value_mut() else {
20 return;
21 };
22
23 let Some(trace_context) = contexts.get_mut::<TraceContext>() else {
24 return;
25 };
26
27 let Some(root_span_id) = trace_context.span_id.value() else {
28 return;
29 };
30
31 let valid_span_ids = spans
32 .iter()
33 .filter_map(|span| span.value())
34 .filter_map(|span| span.span_id.value())
35 .chain(Some(root_span_id))
36 .cloned()
37 .collect::<BTreeSet<_>>();
38
39 for span in spans {
40 let Some(span) = span.value_mut() else {
41 continue;
42 };
43
44 let Some(parent_span_id) = span.parent_span_id.value_mut() else {
45 continue;
46 };
47
48 if valid_span_ids.contains(&*parent_span_id) {
49 continue;
50 };
51
52 let invalid_parent = mem::replace(parent_span_id, root_span_id.clone());
53 let meta = span.parent_span_id.meta_mut();
54 meta.add_error(Error::invalid("span ID does not exist"));
55 meta.set_original_value(Some(invalid_parent));
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use relay_protocol::FromValue;
62 use relay_protocol::SerializableAnnotated;
63
64 use super::*;
65
66 #[test]
67 fn basic() {
68 let mut data = Event::from_value(
69 serde_json::json!({
70 "type": "transaction",
71 "start_timestamp": 1609455600,
72 "end_timestamp": 1609455605,
73 "contexts": {
74 "trace": {
75 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
76 "span_id": "aaaaaaaaaaaaaaaa",
77 "parent_span_id": "ffffffffffffffff",
78 }
79 },
80 "spans": [
81 {
82 "span_id": "bbbbbbbbbbbbbbbb",
83 "parent_span_id": "aaaaaaaaaaaaaaaa"
84 },
85 {
86 "span_id": "bbbbbbbbbbbbbbbb",
87 "parent_span_id": "dddddddddddddddd",
88 },
89 {
90 "span_id": "bbbbbbbbbbbbbbbb",
91 "parent_span_id": "eeeeeeeeeeeeeeee",
92 },
93 ],
94 })
95 .into(),
96 );
97
98 reparent_broken_spans(data.value_mut().as_mut().unwrap());
99
100 insta::assert_json_snapshot!(SerializableAnnotated(&data), @r###"
101 {
102 "type": "transaction",
103 "start_timestamp": 1609455600.0,
104 "contexts": {
105 "trace": {
106 "trace_id": "4c79f60c11214eb38604f4ae0781bfb2",
107 "span_id": "aaaaaaaaaaaaaaaa",
108 "parent_span_id": "ffffffffffffffff",
109 "type": "trace"
110 }
111 },
112 "spans": [
113 {
114 "span_id": "bbbbbbbbbbbbbbbb",
115 "parent_span_id": "aaaaaaaaaaaaaaaa"
116 },
117 {
118 "span_id": "bbbbbbbbbbbbbbbb",
119 "parent_span_id": "aaaaaaaaaaaaaaaa"
120 },
121 {
122 "span_id": "bbbbbbbbbbbbbbbb",
123 "parent_span_id": "aaaaaaaaaaaaaaaa"
124 }
125 ],
126 "end_timestamp": 1609455605,
127 "_meta": {
128 "spans": {
129 "1": {
130 "parent_span_id": {
131 "": {
132 "err": [
133 [
134 "invalid_data",
135 {
136 "reason": "span ID does not exist"
137 }
138 ]
139 ],
140 "val": "dddddddddddddddd"
141 }
142 }
143 },
144 "2": {
145 "parent_span_id": {
146 "": {
147 "err": [
148 [
149 "invalid_data",
150 {
151 "reason": "span ID does not exist"
152 }
153 ]
154 ],
155 "val": "eeeeeeeeeeeeeeee"
156 }
157 }
158 }
159 }
160 }
161 }
162 "###);
163 }
164}