awssdk_instrumentation/lambda/layer/
utils.rs1use opentelemetry::{SpanId, TraceFlags, TraceId};
4
5#[derive(Debug)]
7pub(super) struct XRayTraceHeader {
8 pub(super) trace_id: TraceId,
9 pub(super) parent_id: SpanId,
10 pub(super) sampled: TraceFlags,
11}
12impl XRayTraceHeader {
13 const ROOT: &str = "Root";
15 const PARENT: &str = "Parent";
17 const SAMPLE: &str = "Sampled";
19 const LINEAGE: &str = "Lineage";
21 const HEADER_DELIMITER: &str = ";";
23}
24impl core::str::FromStr for XRayTraceHeader {
26 type Err = String;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 let mut xray_header = Self {
30 trace_id: TraceId::INVALID,
31 parent_id: SpanId::INVALID,
32 sampled: TraceFlags::SAMPLED,
33 };
34 let mut trace_id_collected = false;
35 let mut parent_id_collected = false;
36 let mut sampled_collected = false;
37
38 fn map_err(e: impl ToString) -> String {
39 e.to_string()
40 }
41 for (key, value) in s
42 .split(Self::HEADER_DELIMITER)
43 .filter_map(|part| part.split_once('='))
44 {
45 match key {
46 Self::ROOT if !trace_id_collected => {
47 xray_header.trace_id =
48 TraceId::from_hex(&value.split('-').skip(1).collect::<String>())
49 .map_err(map_err)?;
50 trace_id_collected = true;
51 }
52 Self::PARENT if !parent_id_collected => {
53 xray_header.parent_id = SpanId::from_hex(value).map_err(map_err)?;
54 parent_id_collected = true;
55 }
56 Self::SAMPLE if !sampled_collected => {
57 xray_header.sampled = match value {
58 "0" => TraceFlags::NOT_SAMPLED,
59 "1" => TraceFlags::SAMPLED,
60 _ => return Err("Invalid Trace header".to_owned()),
61 };
62 sampled_collected = true;
63 }
64 Self::LINEAGE => {
65 }
67 _ => {}
70 }
71 }
72
73 if !(trace_id_collected && parent_id_collected && sampled_collected) {
74 return Err("Invalid Trace header".to_owned());
75 }
76
77 Ok(xray_header)
78 }
79}
80
81#[derive(Debug, Default, Clone, Copy)]
108#[non_exhaustive]
109pub enum OTelFaasTrigger {
110 #[default]
115 Datasource,
116 Http,
120 PubSub,
124 Timer,
129 Other,
131}
132
133impl std::fmt::Display for OTelFaasTrigger {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 OTelFaasTrigger::Datasource => write!(f, "datasource"),
139 OTelFaasTrigger::Http => write!(f, "http"),
140 OTelFaasTrigger::PubSub => write!(f, "pubsub"),
141 OTelFaasTrigger::Timer => write!(f, "timer"),
142 OTelFaasTrigger::Other => write!(f, "other"),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use opentelemetry::{SpanId, TraceFlags, TraceId};
151
152 #[test]
155 fn xray_trace_header_valid_sampled_1() {
156 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1";
157 let parsed: XRayTraceHeader = header.parse().unwrap();
158
159 assert_eq!(
160 parsed.trace_id,
161 TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
162 );
163 assert_eq!(
164 parsed.parent_id,
165 SpanId::from_hex("53995c3f42cd8ad8").unwrap()
166 );
167 assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
168 }
169
170 #[test]
171 fn xray_trace_header_valid_sampled_0() {
172 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=0";
173 let parsed: XRayTraceHeader = header.parse().unwrap();
174
175 assert_eq!(
176 parsed.trace_id,
177 TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
178 );
179 assert_eq!(
180 parsed.parent_id,
181 SpanId::from_hex("53995c3f42cd8ad8").unwrap()
182 );
183 assert_eq!(parsed.sampled, TraceFlags::NOT_SAMPLED);
184 }
185
186 #[test]
187 fn xray_trace_header_valid_with_lineage_field() {
188 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;Lineage=a87bd80c:1|68fd508a:5|c512fbe3:2";
189 let parsed: XRayTraceHeader = header.parse().unwrap();
190
191 assert_eq!(
192 parsed.trace_id,
193 TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
194 );
195 assert_eq!(
196 parsed.parent_id,
197 SpanId::from_hex("53995c3f42cd8ad8").unwrap()
198 );
199 assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
200 }
201
202 #[test]
203 fn xray_trace_header_valid_with_unknown_fields() {
204 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;FutureField=somevalue";
205 let parsed: XRayTraceHeader = header.parse().unwrap();
206
207 assert_eq!(
208 parsed.trace_id,
209 TraceId::from_hex("5759e988bd862e3fe1be46a994272793").unwrap()
210 );
211 assert_eq!(
212 parsed.parent_id,
213 SpanId::from_hex("53995c3f42cd8ad8").unwrap()
214 );
215 assert_eq!(parsed.sampled, TraceFlags::SAMPLED);
216 }
217
218 #[test]
219 fn xray_trace_header_missing_parent_field() {
220 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1";
221 let result: Result<XRayTraceHeader, _> = header.parse();
222 assert!(result.is_err());
223 }
224
225 #[test]
226 fn xray_trace_header_invalid_sampled_value() {
227 let header = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=2";
228 let result: Result<XRayTraceHeader, _> = header.parse();
229 assert!(result.is_err());
230 }
231
232 #[test]
233 fn xray_trace_header_empty_string() {
234 let result: Result<XRayTraceHeader, _> = "".parse();
235 assert!(result.is_err());
236 }
237}