Skip to main content

libdd_trace_utils/span/v1/
mod.rs

1// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::span::vec_map::VecMap;
5use crate::span::{BytesData, SliceData, TraceData};
6pub use thin_vec::ThinVec;
7
8/// OpenTelemetry SpanKind values, encoded on the wire as a `uint32`.
9/// Unset or unrecognized kinds default to [`SpanKind::Internal`].
10#[repr(u32)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum SpanKind {
13    #[default]
14    Internal = 1,
15    Server = 2,
16    Client = 3,
17    Producer = 4,
18    Consumer = 5,
19}
20
21impl SpanKind {
22    /// Parses a v0.4 `span.kind` meta value into a [`SpanKind`].
23    /// Unrecognized values map to [`SpanKind::Internal`].
24    pub fn from_meta(s: &str) -> Self {
25        match s {
26            "server" => SpanKind::Server,
27            "client" => SpanKind::Client,
28            "producer" => SpanKind::Producer,
29            "consumer" => SpanKind::Consumer,
30            _ => SpanKind::Internal,
31        }
32    }
33}
34
35/// Typed V1 attribute value.
36/// Replaces v0.4's split `meta` / `metrics` / `meta_struct` maps.
37#[derive(Debug)]
38pub enum AttributeValue<T: TraceData> {
39    String(T::Text),
40    Float(f64),
41    Int(i64),
42    Bool(bool),
43    Bytes(T::Bytes),
44    KeyValue(VecMap<T::Text, AttributeValue<T>>),
45    List(Vec<AttributeValue<T>>),
46}
47
48/// The generic representation of a V1 span.
49///
50/// `T: TraceData` carries the associated text type `T::Text` used for every string field in the
51/// span; `T::Text` can be either owned (e.g. [`BytesString`](libdd_tinybytes::BytesString)) or
52/// borrowed (e.g. `&str`). To define a generic function taking any `Span<T>` you can use the
53/// [`TraceData`] trait:
54/// ```
55/// use libdd_trace_utils::span::{v1::Span, TraceData};
56/// fn foo<T: TraceData>(span: Span<T>) {
57///     let _ = span.attributes.get("foo");
58/// }
59/// ```
60#[derive(Debug, Default)]
61pub struct Span<T: TraceData> {
62    pub service: T::Text,
63    pub name: T::Text,
64    pub resource: T::Text,
65    pub r#type: T::Text,
66    pub span_id: u64,
67    pub parent_id: u64,
68    pub start: i64,
69    pub duration: i64,
70    pub error: bool,
71    pub span_kind: SpanKind,
72    pub env: T::Text,
73    pub version: T::Text,
74    pub component: T::Text,
75    pub attributes: VecMap<T::Text, AttributeValue<T>>,
76    pub span_links: ThinVec<SpanLink<T>>,
77    pub span_events: ThinVec<SpanEvent<T>>,
78}
79
80/// The generic representation of a V1 span link.
81/// `T` is the type used to represent strings in the span link.
82#[derive(Debug, Default)]
83pub struct SpanLink<T: TraceData> {
84    pub trace_id: [u8; 16],
85    pub span_id: u64,
86    pub attributes: VecMap<T::Text, AttributeValue<T>>,
87    pub tracestate: T::Text,
88    pub flags: u32,
89}
90
91/// The generic representation of a V1 span event.
92/// `T` is the type used to represent strings in the span event.
93#[derive(Debug, Default)]
94pub struct SpanEvent<T: TraceData> {
95    pub time_unix_nano: u64,
96    pub name: T::Text,
97    pub attributes: VecMap<T::Text, AttributeValue<T>>,
98}
99
100/// A V1 trace chunk: a group of spans sharing the same `trace_id`, plus chunk-level metadata.
101#[derive(Debug, Default)]
102pub struct TraceChunk<T: TraceData> {
103    pub trace_id: [u8; 16],
104    pub priority: Option<i32>,
105    pub origin: T::Text,
106    pub sampling_mechanism: Option<u32>,
107    pub dropped_trace: bool,
108    pub attributes: VecMap<T::Text, AttributeValue<T>>,
109    pub spans: Vec<Span<T>>,
110}
111
112/// A V1 tracer payload: tracer-level metadata and the trace chunks it carries.
113#[derive(Debug, Default)]
114pub struct TracerPayload<T: TraceData> {
115    pub language_name: T::Text,
116    pub language_version: T::Text,
117    pub tracer_version: T::Text,
118    pub runtime_id: T::Text,
119    pub env: T::Text,
120    pub hostname: T::Text,
121    pub app_version: T::Text,
122    pub attributes: VecMap<T::Text, AttributeValue<T>>,
123    pub chunks: Vec<TraceChunk<T>>,
124}
125
126pub type SpanBytes = Span<BytesData>;
127pub type SpanLinkBytes = SpanLink<BytesData>;
128pub type SpanEventBytes = SpanEvent<BytesData>;
129pub type AttributeValueBytes = AttributeValue<BytesData>;
130pub type TraceChunkBytes = TraceChunk<BytesData>;
131pub type TracerPayloadBytes = TracerPayload<BytesData>;
132
133pub type SpanSlice<'a> = Span<SliceData<'a>>;
134pub type SpanLinkSlice<'a> = SpanLink<SliceData<'a>>;
135pub type SpanEventSlice<'a> = SpanEvent<SliceData<'a>>;
136pub type AttributeValueSlice<'a> = AttributeValue<SliceData<'a>>;
137pub type TraceChunkSlice<'a> = TraceChunk<SliceData<'a>>;
138pub type TracerPayloadSlice<'a> = TracerPayload<SliceData<'a>>;
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn span_kind_default_is_internal() {
146        assert_eq!(SpanKind::default(), SpanKind::Internal);
147    }
148
149    #[test]
150    fn span_kind_from_meta() {
151        assert_eq!(SpanKind::from_meta("server"), SpanKind::Server);
152        assert_eq!(SpanKind::from_meta("client"), SpanKind::Client);
153        assert_eq!(SpanKind::from_meta("producer"), SpanKind::Producer);
154        assert_eq!(SpanKind::from_meta("consumer"), SpanKind::Consumer);
155        assert_eq!(SpanKind::from_meta("internal"), SpanKind::Internal);
156        assert_eq!(SpanKind::from_meta(""), SpanKind::Internal);
157        assert_eq!(SpanKind::from_meta("anything-else"), SpanKind::Internal);
158    }
159
160    #[test]
161    fn span_kind_repr_matches_otel_spec() {
162        assert_eq!(SpanKind::Internal as u32, 1);
163        assert_eq!(SpanKind::Server as u32, 2);
164        assert_eq!(SpanKind::Client as u32, 3);
165        assert_eq!(SpanKind::Producer as u32, 4);
166        assert_eq!(SpanKind::Consumer as u32, 5);
167    }
168
169    #[test]
170    fn span_default_has_internal_kind() {
171        let s = SpanBytes::default();
172        assert_eq!(s.span_kind, SpanKind::Internal);
173        assert!(!s.error);
174        assert!(s.attributes.is_empty());
175    }
176}