Skip to main content

libdd_trace_utils/span/v04/
mod.rs

1// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::span::{BytesData, SliceData, SpanKeyParseError, TraceData};
5use crate::tracer_payload::TraceChunks;
6use serde::ser::SerializeStruct;
7use serde::Serialize;
8use std::borrow::Borrow;
9use std::collections::HashMap;
10use std::str::FromStr;
11
12pub use super::vec_map::VecMap;
13
14#[derive(Debug, PartialEq)]
15pub enum SpanKey {
16    Service,
17    Name,
18    Resource,
19    TraceId,
20    SpanId,
21    ParentId,
22    Start,
23    Duration,
24    Error,
25    Meta,
26    Metrics,
27    Type,
28    MetaStruct,
29    SpanLinks,
30    SpanEvents,
31}
32
33impl FromStr for SpanKey {
34    type Err = SpanKeyParseError;
35
36    fn from_str(s: &str) -> Result<Self, Self::Err> {
37        match s {
38            "service" => Ok(SpanKey::Service),
39            "name" => Ok(SpanKey::Name),
40            "resource" => Ok(SpanKey::Resource),
41            "trace_id" => Ok(SpanKey::TraceId),
42            "span_id" => Ok(SpanKey::SpanId),
43            "parent_id" => Ok(SpanKey::ParentId),
44            "start" => Ok(SpanKey::Start),
45            "duration" => Ok(SpanKey::Duration),
46            "error" => Ok(SpanKey::Error),
47            "meta" => Ok(SpanKey::Meta),
48            "metrics" => Ok(SpanKey::Metrics),
49            "type" => Ok(SpanKey::Type),
50            "meta_struct" => Ok(SpanKey::MetaStruct),
51            "span_links" => Ok(SpanKey::SpanLinks),
52            "span_events" => Ok(SpanKey::SpanEvents),
53            _ => Err(SpanKeyParseError::new(format!("Invalid span key: {s}"))),
54        }
55    }
56}
57
58/// Checks if the `value` represents an empty string. Used to skip serializing empty strings
59/// with serde.
60fn is_empty_str<T: Borrow<str>>(value: &T) -> bool {
61    value.borrow().is_empty()
62}
63
64/// The generic representation of a V04 span.
65///
66/// `T` is the type used to represent strings in the span, it can be either owned (e.g. BytesString)
67/// or borrowed (e.g. &str). To define a generic function taking any `Span<T>` you can use the
68/// [`SpanValue`] trait:
69/// ```
70/// use libdd_trace_utils::span::{v04::Span, TraceData};
71/// fn foo<T: TraceData>(span: Span<T>) {
72///     let _ = span.meta.get("foo");
73/// }
74/// ```
75#[derive(Debug, Default, Serialize)]
76#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq))]
77pub struct Span<T: TraceData> {
78    pub service: T::Text,
79    pub name: T::Text,
80    pub resource: T::Text,
81    #[serde(skip_serializing_if = "is_empty_str")]
82    pub r#type: T::Text,
83    #[serde(serialize_with = "serialize_lower_64_bits")]
84    pub trace_id: u128,
85    pub span_id: u64,
86    #[serde(skip_serializing_if = "is_default")]
87    pub parent_id: u64,
88    pub start: i64,
89    pub duration: i64,
90    #[serde(skip_serializing_if = "is_default")]
91    pub error: i32,
92    #[serde(skip_serializing_if = "VecMap::is_empty")]
93    pub meta: VecMap<T::Text, T::Text>,
94    #[serde(skip_serializing_if = "VecMap::is_empty")]
95    pub metrics: VecMap<T::Text, f64>,
96    #[serde(skip_serializing_if = "VecMap::is_empty")]
97    pub meta_struct: VecMap<T::Text, T::Bytes>,
98    #[serde(skip_serializing_if = "Vec::is_empty")]
99    pub span_links: Vec<SpanLink<T>>,
100    #[serde(skip_serializing_if = "Vec::is_empty")]
101    pub span_events: Vec<SpanEvent<T>>,
102}
103
104impl<T: TraceData> Clone for Span<T>
105where
106    T::Text: Clone,
107    T::Bytes: Clone,
108{
109    fn clone(&self) -> Self {
110        Span {
111            service: self.service.clone(),
112            name: self.name.clone(),
113            resource: self.resource.clone(),
114            r#type: self.r#type.clone(),
115            trace_id: self.trace_id,
116            span_id: self.span_id,
117            parent_id: self.parent_id,
118            start: self.start,
119            duration: self.duration,
120            error: self.error,
121            meta: self.meta.clone(),
122            metrics: self.metrics.clone(),
123            meta_struct: self.meta_struct.clone(),
124            span_links: self.span_links.clone(),
125            span_events: self.span_events.clone(),
126        }
127    }
128}
129
130fn serialize_lower_64_bits<S>(v: &u128, serializer: S) -> Result<S::Ok, S::Error>
131where
132    S: serde::Serializer,
133{
134    serializer.serialize_u64(*v as u64)
135}
136
137/// The generic representation of a V04 span link.
138/// `T` is the type used to represent strings in the span link.
139#[derive(Debug, Default, PartialEq, Serialize)]
140pub struct SpanLink<T: TraceData> {
141    pub trace_id: u64,
142    pub trace_id_high: u64,
143    pub span_id: u64,
144    #[serde(skip_serializing_if = "HashMap::is_empty")]
145    pub attributes: HashMap<T::Text, T::Text>,
146    #[serde(skip_serializing_if = "is_empty_str")]
147    pub tracestate: T::Text,
148    #[serde(skip_serializing_if = "is_default")]
149    pub flags: u32,
150}
151
152impl<T: TraceData> Clone for SpanLink<T>
153where
154    T::Text: Clone,
155{
156    fn clone(&self) -> Self {
157        SpanLink {
158            trace_id: self.trace_id,
159            trace_id_high: self.trace_id_high,
160            span_id: self.span_id,
161            attributes: self.attributes.clone(),
162            tracestate: self.tracestate.clone(),
163            flags: self.flags,
164        }
165    }
166}
167
168/// The generic representation of a V04 span event.
169/// `T` is the type used to represent strings in the span event.
170#[derive(Debug, Default, PartialEq, Serialize)]
171pub struct SpanEvent<T: TraceData> {
172    pub time_unix_nano: u64,
173    pub name: T::Text,
174    #[serde(skip_serializing_if = "HashMap::is_empty")]
175    pub attributes: HashMap<T::Text, AttributeAnyValue<T>>,
176}
177
178impl<T: TraceData> Clone for SpanEvent<T>
179where
180    T::Text: Clone,
181{
182    fn clone(&self) -> Self {
183        SpanEvent {
184            time_unix_nano: self.time_unix_nano,
185            name: self.name.clone(),
186            attributes: self.attributes.clone(),
187        }
188    }
189}
190
191#[derive(Debug, PartialEq)]
192pub enum AttributeAnyValue<T: TraceData> {
193    SingleValue(AttributeArrayValue<T>),
194    Array(Vec<AttributeArrayValue<T>>),
195}
196
197#[derive(Serialize)]
198struct ArrayValueWrapper<'a, T: TraceData> {
199    #[serde(bound(serialize = "T::Text: Serialize"))]
200    values: &'a Vec<AttributeArrayValue<T>>,
201}
202
203impl<T: TraceData> Serialize for AttributeAnyValue<T> {
204    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
205    where
206        S: serde::Serializer,
207    {
208        let mut state = serializer.serialize_struct("AttributeAnyValue", 2)?;
209
210        match self {
211            AttributeAnyValue::SingleValue(attribute) => {
212                serialize_attribute_array::<S, T>(&mut state, attribute)?;
213            }
214            AttributeAnyValue::Array(value) => {
215                let value_type: u8 = self.into();
216                state.serialize_field("type", &value_type)?;
217                let wrapped_value = ArrayValueWrapper { values: value };
218                state.serialize_field("array_value", &wrapped_value)?;
219            }
220        }
221
222        state.end()
223    }
224}
225
226impl<T: TraceData> From<&AttributeAnyValue<T>> for u8 {
227    fn from(attribute: &AttributeAnyValue<T>) -> u8 {
228        match attribute {
229            AttributeAnyValue::SingleValue(value) => value.into(),
230            AttributeAnyValue::Array(_) => 4,
231        }
232    }
233}
234
235impl<T: TraceData> Clone for AttributeAnyValue<T>
236where
237    T::Text: Clone,
238{
239    fn clone(&self) -> Self {
240        match self {
241            AttributeAnyValue::SingleValue(v) => AttributeAnyValue::SingleValue(v.clone()),
242            AttributeAnyValue::Array(vec) => AttributeAnyValue::Array(vec.clone()),
243        }
244    }
245}
246
247#[derive(Debug, PartialEq)]
248pub enum AttributeArrayValue<T: TraceData> {
249    String(T::Text),
250    Boolean(bool),
251    Integer(i64),
252    Double(f64),
253}
254
255impl<T: TraceData> Clone for AttributeArrayValue<T>
256where
257    T::Text: Clone,
258{
259    fn clone(&self) -> Self {
260        match self {
261            AttributeArrayValue::String(v) => AttributeArrayValue::String(v.clone()),
262            AttributeArrayValue::Boolean(v) => AttributeArrayValue::Boolean(*v),
263            AttributeArrayValue::Integer(v) => AttributeArrayValue::Integer(*v),
264            AttributeArrayValue::Double(v) => AttributeArrayValue::Double(*v),
265        }
266    }
267}
268
269impl<T: TraceData> Serialize for AttributeArrayValue<T> {
270    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
271    where
272        S: serde::Serializer,
273    {
274        let mut state = serializer.serialize_struct("AttributeArrayValue", 2)?;
275        serialize_attribute_array::<S, T>(&mut state, self)?;
276        state.end()
277    }
278}
279
280fn serialize_attribute_array<S, T>(
281    state: &mut S::SerializeStruct,
282    attribute: &AttributeArrayValue<T>,
283) -> Result<(), <S>::Error>
284where
285    T: TraceData,
286    S: serde::Serializer,
287{
288    let attribute_type: u8 = attribute.into();
289    state.serialize_field("type", &attribute_type)?;
290    match attribute {
291        AttributeArrayValue::String(value) => state.serialize_field("string_value", value),
292        AttributeArrayValue::Boolean(value) => state.serialize_field("bool_value", value),
293        AttributeArrayValue::Integer(value) => state.serialize_field("int_value", value),
294        AttributeArrayValue::Double(value) => state.serialize_field("double_value", value),
295    }
296}
297
298impl<T: TraceData> From<&AttributeArrayValue<T>> for u8 {
299    fn from(attribute: &AttributeArrayValue<T>) -> u8 {
300        match attribute {
301            AttributeArrayValue::String(_) => 0,
302            AttributeArrayValue::Boolean(_) => 1,
303            AttributeArrayValue::Integer(_) => 2,
304            AttributeArrayValue::Double(_) => 3,
305        }
306    }
307}
308
309impl<T: TraceData> Span<T> {
310    /// Deduplicate the [VecMap] parts of this span. See [VecMap::dedup].
311    pub fn dedup(&mut self) {
312        self.meta.dedup();
313        self.metrics.dedup();
314        self.meta_struct.dedup();
315    }
316}
317
318fn is_default<T: Default + PartialEq>(t: &T) -> bool {
319    t == &T::default()
320}
321
322pub type SpanBytes = Span<BytesData>;
323pub type SpanLinkBytes = SpanLink<BytesData>;
324pub type SpanEventBytes = SpanEvent<BytesData>;
325pub type AttributeAnyValueBytes = AttributeAnyValue<BytesData>;
326pub type AttributeArrayValueBytes = AttributeArrayValue<BytesData>;
327
328pub type SpanSlice<'a> = Span<SliceData<'a>>;
329pub type SpanLinkSlice<'a> = SpanLink<SliceData<'a>>;
330pub type SpanEventSlice<'a> = SpanEvent<SliceData<'a>>;
331pub type AttributeAnyValueSlice<'a> = AttributeAnyValue<SliceData<'a>>;
332pub type AttributeArrayValueSlice<'a> = AttributeArrayValue<SliceData<'a>>;
333
334pub type TraceChunksBytes = TraceChunks<BytesData>;
335
336#[cfg(test)]
337mod tests {
338    use super::{AttributeAnyValue, AttributeArrayValue, Span, SpanEvent, SpanLink};
339    use crate::msgpack_decoder::decode::buffer::Buffer;
340    use crate::msgpack_decoder::v04::span::decode_span;
341    use crate::span::SliceData;
342    use std::collections::HashMap;
343
344    #[test]
345    fn skip_serializing_empty_fields_test() {
346        let expected = b"\x87\xa7service\xa0\xa4name\xa0\xa8resource\xa0\xa8trace_id\x00\xa7span_id\x00\xa5start\x00\xa8duration\x00";
347        let val: Span<SliceData<'_>> = Span::default();
348        let serialized = rmp_serde::encode::to_vec_named(&val).unwrap();
349        assert_eq!(expected, serialized.as_slice());
350    }
351
352    #[test]
353    fn serialize_deserialize_test() {
354        let span: Span<SliceData<'_>> = Span {
355            name: "tracing.operation",
356            resource: "MyEndpoint",
357            span_links: vec![SpanLink {
358                trace_id: 42,
359                attributes: HashMap::from([("span", "link")]),
360                tracestate: "running",
361                ..Default::default()
362            }],
363            span_events: vec![SpanEvent {
364                time_unix_nano: 1727211691770716000,
365                name: "exception",
366                attributes: HashMap::from([
367                    (
368                        "exception.message",
369                        AttributeAnyValue::SingleValue(AttributeArrayValue::String(
370                            "Cannot divide by zero",
371                        )),
372                    ),
373                    (
374                        "exception.type",
375                        AttributeAnyValue::SingleValue(AttributeArrayValue::String("RuntimeError")),
376                    ),
377                    (
378                        "exception.escaped",
379                        AttributeAnyValue::SingleValue(AttributeArrayValue::Boolean(false)),
380                    ),
381                    (
382                        "exception.count",
383                        AttributeAnyValue::SingleValue(AttributeArrayValue::Integer(1)),
384                    ),
385                    (
386                        "exception.lines",
387                        AttributeAnyValue::Array(vec![
388                            AttributeArrayValue::String("  File \"<string>\", line 1, in <module>"),
389                            AttributeArrayValue::String("  File \"<string>\", line 1, in divide"),
390                            AttributeArrayValue::String("RuntimeError: Cannot divide by zero"),
391                        ]),
392                    ),
393                ]),
394            }],
395            ..Default::default()
396        };
397
398        let serialized = rmp_serde::encode::to_vec_named(&span).unwrap();
399        let mut serialized_slice = Buffer::<SliceData<'_>>::new(serialized.as_ref());
400        let deserialized = decode_span(&mut serialized_slice).unwrap();
401
402        assert_eq!(span.name, deserialized.name);
403        assert_eq!(span.resource, deserialized.resource);
404        assert_eq!(
405            span.span_links[0].trace_id,
406            deserialized.span_links[0].trace_id
407        );
408        assert_eq!(
409            span.span_links[0].tracestate,
410            deserialized.span_links[0].tracestate
411        );
412        assert_eq!(span.span_events[0].name, deserialized.span_events[0].name);
413        assert_eq!(
414            span.span_events[0].time_unix_nano,
415            deserialized.span_events[0].time_unix_nano
416        );
417        for attribut in &deserialized.span_events[0].attributes {
418            assert!(span.span_events[0].attributes.contains_key(attribut.0))
419        }
420    }
421
422    #[test]
423    fn serialize_event_test() {
424        // `expected` is created by transforming the span into bytes
425        // and passing each bytes through `escaped_default`
426        let expected = b"\x88\xa7service\xa0\xa4name\xa0\xa8resource\xa0\xa8trace_id\x00\xa7span_id\x00\xa5start\x00\xa8duration\x00\xabspan_events\x91\x83\xaetime_unix_nano\xcf\x17\xf8I\xe1\xeb\xe5\x1f`\xa4name\xa4test\xaaattributes\x81\xaatest.event\x82\xa4type\x03\xacdouble_value\xcb@\x10\xcc\xcc\xcc\xcc\xcc\xcd";
427
428        let span: Span<SliceData<'_>> = Span {
429            span_events: vec![SpanEvent {
430                time_unix_nano: 1727211691770716000,
431                name: "test",
432                attributes: HashMap::from([(
433                    "test.event",
434                    AttributeAnyValue::SingleValue(AttributeArrayValue::Double(4.2)),
435                )]),
436            }],
437            ..Default::default()
438        };
439
440        let serialized = rmp_serde::encode::to_vec_named(&span).unwrap();
441        assert_eq!(expected, serialized.as_slice());
442    }
443}