Skip to main content

saluki_core/data_model/event/trace/
mod.rs

1//! Traces.
2
3use std::sync::Arc;
4
5use saluki_common::collections::FastHashMap;
6use stringtheory::MetaString;
7
8/// Typed value for attributes at every level of the trace model: span attributes,
9/// span event attributes, span link attributes, and trace-level attributes.
10///
11/// Covers all variants carried by the V1 APM `idx` wire format (`RawAnyValue`).
12#[derive(Clone, Debug, PartialEq)]
13pub enum AttributeValue {
14    /// String-valued attribute.
15    String(MetaString),
16    /// Boolean attribute.
17    Bool(bool),
18    /// Integer attribute.
19    Int(i64),
20    /// Floating-point attribute.
21    Float(f64),
22    /// Raw bytes attribute.
23    Bytes(Vec<u8>),
24    /// Array of attribute values (may be heterogeneous).
25    Array(Vec<AttributeValue>),
26    /// List of key-value pairs.
27    KeyValueList(Vec<(MetaString, AttributeValue)>),
28}
29
30impl AttributeValue {
31    /// Returns the inner string if this is a `String` variant.
32    pub fn as_string(&self) -> Option<&MetaString> {
33        if let AttributeValue::String(s) = self {
34            Some(s)
35        } else {
36            None
37        }
38    }
39
40    /// Returns the inner float if this is a `Float` variant.
41    ///
42    /// Returns `Some` only when the stored variant is `Float`. For numeric semantic tags
43    /// where the source may store an integer (for example, sampling priority, `_sample_rate`,
44    /// `_top_level`), use [`as_num`][AttributeValue::as_num] instead.
45    pub fn as_float(&self) -> Option<f64> {
46        if let AttributeValue::Float(f) = self {
47            Some(*f)
48        } else {
49            None
50        }
51    }
52
53    /// Returns the inner bool if this is a `Bool` variant.
54    pub fn as_bool(&self) -> Option<bool> {
55        if let AttributeValue::Bool(b) = self {
56            Some(*b)
57        } else {
58            None
59        }
60    }
61
62    /// Returns the inner integer if this is an `Int` variant.
63    pub fn as_int(&self) -> Option<i64> {
64        if let AttributeValue::Int(i) = self {
65            Some(*i)
66        } else {
67            None
68        }
69    }
70
71    /// Returns a numeric value as `f64` for either `Float` or `Int` variants.
72    ///
73    /// Use this when the caller only needs a number and doesn't care whether
74    /// the stored type is integral or floating-point (for example, sampling priority).
75    pub fn as_num(&self) -> Option<f64> {
76        match self {
77            AttributeValue::Float(f) => Some(*f),
78            AttributeValue::Int(i) => Some(*i as f64),
79            _ => None,
80        }
81    }
82
83    /// Returns the inner bytes if this is a `Bytes` variant.
84    pub fn as_bytes(&self) -> Option<&[u8]> {
85        if let AttributeValue::Bytes(b) = self {
86            Some(b)
87        } else {
88            None
89        }
90    }
91}
92
93/// Payload-level metadata promoted from the tracer payload or OTLP resource.
94///
95/// These fields are common to all chunks within a single tracer payload and describe
96/// the tracer and its environment rather than any individual trace or span.
97#[derive(Clone, Debug, PartialEq, Default)]
98pub struct PayloadFields {
99    /// Container ID associated with the tracer.
100    pub container_id: MetaString,
101    /// Tracer language name (for example, `"go"`, `"python"`).
102    pub language_name: MetaString,
103    /// Tracer language runtime version.
104    pub language_version: MetaString,
105    /// Tracer library version.
106    pub tracer_version: MetaString,
107    /// Tracer runtime ID.
108    pub runtime_id: MetaString,
109    /// Deployment environment (for example, `"production"`, `"staging"`).
110    pub env: MetaString,
111    /// Hostname of the tracer host.
112    pub hostname: MetaString,
113    /// Application version string.
114    pub app_version: MetaString,
115    /// Per-chunk weight from `Datadog-Client-Dropped-P0-Traces` header. Zero if absent.
116    pub client_dropped_p0s_weight: f64,
117}
118
119/// A trace event.
120///
121/// A trace is a collection of spans that represent a distributed trace.
122#[derive(Clone, Debug, PartialEq)]
123pub struct Trace {
124    /// The spans that make up this trace.
125    spans: Vec<Span>,
126    /// Upper 8 bytes of the 128-bit trace ID (big-endian). Zero for 64-bit-only sources.
127    pub trace_id_high: u64,
128    /// Lower 8 bytes of the 128-bit trace ID (big-endian).
129    pub trace_id_low: u64,
130    /// Trace origin string (for example, `"lambda"`, `"rum"`).
131    pub origin: MetaString,
132    /// Payload-level metadata (promoted from the tracer payload or OTLP resource).
133    pub payload: PayloadFields,
134    /// Trace chunk-level or resource-level attributes.
135    pub attributes: Arc<FastHashMap<MetaString, AttributeValue>>,
136    /// Sampling priority set by the tracer or a sampler.
137    pub priority: Option<i32>,
138    /// Whether this trace was dropped during sampling.
139    pub dropped_trace: bool,
140    /// The mechanism by which the sampling decision was made.
141    pub sampling_mechanism: u32,
142    /// Identifier of the component that made the final sampling decision.
143    pub decision_maker: Option<MetaString>,
144    /// Effective OTLP sampling rate (`_dd.otlp_sr`), if set.
145    pub otlp_sampling_rate: Option<f64>,
146}
147
148impl Trace {
149    /// Creates a new `Trace` with the given spans.
150    ///
151    /// All unified fields default to empty / zero. Callers should set them
152    /// directly after construction.
153    pub fn new(spans: Vec<Span>) -> Self {
154        Self {
155            spans,
156            trace_id_high: 0,
157            trace_id_low: 0,
158            origin: MetaString::empty(),
159            payload: PayloadFields::default(),
160            attributes: Arc::new(FastHashMap::default()),
161            priority: None,
162            dropped_trace: false,
163            sampling_mechanism: 0,
164            decision_maker: None,
165            otlp_sampling_rate: None,
166        }
167    }
168
169    /// Returns a reference to the spans in this trace.
170    pub fn spans(&self) -> &[Span] {
171        &self.spans
172    }
173
174    /// Returns a mutable reference to the spans in this trace.
175    pub fn spans_mut(&mut self) -> &mut [Span] {
176        &mut self.spans
177    }
178
179    /// Replaces the spans in this trace with the given spans.
180    pub fn set_spans(&mut self, spans: Vec<Span>) {
181        self.spans = spans;
182    }
183
184    /// Retains only the spans specified by the predicate.
185    ///
186    /// Returns the number of spans retained. If no spans match, the trace is left unchanged.
187    pub fn retain_spans<F>(&mut self, mut f: F) -> usize
188    where
189        F: FnMut(&Trace, &Span) -> bool,
190    {
191        if self.spans.is_empty() {
192            return 0;
193        }
194
195        let mut has_match = false;
196        for span in self.spans.iter() {
197            if f(self, span) {
198                has_match = true;
199                break;
200            }
201        }
202
203        if !has_match {
204            return 0;
205        }
206
207        let mut spans = std::mem::take(&mut self.spans);
208        spans.retain(|span| f(self, span));
209        spans.shrink_to_fit();
210        let _ = std::mem::replace(&mut self.spans, spans);
211
212        self.spans.len()
213    }
214
215    /// Remove spans only the spans specified by the predicate return true.
216    pub fn remove_spans<F>(&mut self, mut f: F)
217    where
218        F: FnMut(&Trace, &Span) -> bool,
219    {
220        if self.spans.is_empty() {
221            return;
222        }
223
224        let mut spans = std::mem::take(&mut self.spans);
225        spans.retain(|span| !f(self, span));
226        spans.shrink_to_fit();
227        let _ = std::mem::replace(&mut self.spans, spans);
228    }
229}
230
231/// A span event.
232#[derive(Clone, Debug, PartialEq, Default)]
233pub struct Span {
234    /// The name of the service associated with this span.
235    service: MetaString,
236    /// The operation name of this span.
237    name: MetaString,
238    /// The resource associated with this span.
239    resource: MetaString,
240    /// The unique identifier of this span.
241    span_id: u64,
242    /// The identifier of this span's parent, if any.
243    parent_id: u64,
244    /// The start timestamp of this span in nanoseconds since Unix epoch.
245    start: u64,
246    /// The duration of this span in nanoseconds.
247    duration: u64,
248    /// Error flag represented as 0 (no error) or 1 (error).
249    error: i32,
250    /// Span type classification (for example, web, db, lambda).
251    span_type: MetaString,
252    /// Links describing relationships to other spans.
253    span_links: Vec<SpanLink>,
254    /// Events associated with this span.
255    span_events: Vec<SpanEvent>,
256    /// Per-span environment override. Overrides `Trace.payload.env` when non-empty.
257    pub env: MetaString,
258    /// Per-span application version.
259    pub version: MetaString,
260    /// Instrumentation component name.
261    pub component: MetaString,
262    /// Span kind (OTel values): 0=unspecified, 1=internal, 2=server, 3=client, 4=producer, 5=consumer.
263    pub kind: u32,
264    /// Typed span-level attributes.
265    pub attributes: FastHashMap<MetaString, AttributeValue>,
266}
267
268impl Span {
269    /// Creates a new `Span` with all required fields.
270    #[allow(clippy::too_many_arguments)]
271    pub fn new(
272        service: impl Into<MetaString>, name: impl Into<MetaString>, resource: impl Into<MetaString>,
273        span_type: impl Into<MetaString>, span_id: u64, parent_id: u64, start: u64, duration: u64, error: i32,
274    ) -> Self {
275        Self {
276            service: service.into(),
277            name: name.into(),
278            resource: resource.into(),
279            span_type: span_type.into(),
280            span_id,
281            parent_id,
282            start,
283            duration,
284            error,
285            ..Self::default()
286        }
287    }
288
289    /// Sets the service name.
290    pub fn with_service(mut self, service: impl Into<MetaString>) -> Self {
291        self.service = service.into();
292        self
293    }
294
295    /// Sets the operation name.
296    pub fn with_name(mut self, name: impl Into<MetaString>) -> Self {
297        self.name = name.into();
298        self
299    }
300
301    /// Sets the resource name.
302    pub fn with_resource(mut self, resource: impl Into<MetaString>) -> Self {
303        self.resource = resource.into();
304        self
305    }
306
307    /// Sets the span identifier.
308    pub fn with_span_id(mut self, span_id: u64) -> Self {
309        self.span_id = span_id;
310        self
311    }
312
313    /// Sets the parent span identifier.
314    pub fn with_parent_id(mut self, parent_id: u64) -> Self {
315        self.parent_id = parent_id;
316        self
317    }
318
319    /// Sets the start timestamp.
320    pub fn with_start(mut self, start: u64) -> Self {
321        self.start = start;
322        self
323    }
324
325    /// Sets the span duration.
326    pub fn with_duration(mut self, duration: u64) -> Self {
327        self.duration = duration;
328        self
329    }
330
331    /// Sets the error flag.
332    pub fn with_error(mut self, error: i32) -> Self {
333        self.error = error;
334        self
335    }
336
337    /// Sets the span type (for example, web, db, lambda).
338    pub fn with_span_type(mut self, span_type: impl Into<MetaString>) -> Self {
339        self.span_type = span_type.into();
340        self
341    }
342
343    /// Replaces the span attributes map.
344    pub fn with_attributes(mut self, attributes: impl Into<Option<FastHashMap<MetaString, AttributeValue>>>) -> Self {
345        self.attributes = attributes.into().unwrap_or_default();
346        self
347    }
348
349    /// Replaces the span links collection.
350    pub fn with_span_links(mut self, span_links: impl Into<Option<Vec<SpanLink>>>) -> Self {
351        self.span_links = span_links.into().unwrap_or_default();
352        self
353    }
354
355    /// Replaces the span events collection.
356    pub fn with_span_events(mut self, span_events: impl Into<Option<Vec<SpanEvent>>>) -> Self {
357        self.span_events = span_events.into().unwrap_or_default();
358        self
359    }
360
361    /// Sets the per-span environment override.
362    pub fn with_env(mut self, env: impl Into<MetaString>) -> Self {
363        self.env = env.into();
364        self
365    }
366
367    /// Sets the per-span application version.
368    pub fn with_version(mut self, version: impl Into<MetaString>) -> Self {
369        self.version = version.into();
370        self
371    }
372
373    /// Sets the instrumentation component.
374    pub fn with_component(mut self, component: impl Into<MetaString>) -> Self {
375        self.component = component.into();
376        self
377    }
378
379    /// Sets the span kind.
380    pub fn with_kind(mut self, kind: u32) -> Self {
381        self.kind = kind;
382        self
383    }
384
385    /// Returns the service name.
386    pub fn service(&self) -> &str {
387        &self.service
388    }
389
390    /// Returns the operation name.
391    pub fn name(&self) -> &str {
392        &self.name
393    }
394
395    /// Returns the resource name.
396    pub fn resource(&self) -> &str {
397        &self.resource
398    }
399
400    /// Sets the resource name.
401    pub fn set_resource(&mut self, resource: impl Into<MetaString>) {
402        self.resource = resource.into();
403    }
404
405    /// Returns the span identifier.
406    pub fn span_id(&self) -> u64 {
407        self.span_id
408    }
409
410    /// Returns the parent span identifier.
411    pub fn parent_id(&self) -> u64 {
412        self.parent_id
413    }
414
415    /// Returns the start timestamp.
416    pub fn start(&self) -> u64 {
417        self.start
418    }
419
420    /// Returns the span duration.
421    pub fn duration(&self) -> u64 {
422        self.duration
423    }
424
425    /// Returns the error flag.
426    pub fn error(&self) -> i32 {
427        self.error
428    }
429
430    /// Returns the span type.
431    pub fn span_type(&self) -> &str {
432        &self.span_type
433    }
434
435    /// Returns the span links collection.
436    pub fn span_links(&self) -> &[SpanLink] {
437        &self.span_links
438    }
439
440    /// Returns the span events collection.
441    pub fn span_events(&self) -> &[SpanEvent] {
442        &self.span_events
443    }
444}
445
446/// A link between spans describing a causal relationship.
447#[derive(Clone, Debug, PartialEq, Default)]
448pub struct SpanLink {
449    /// Trace identifier for the linked span.
450    trace_id: u64,
451    /// High bits of the trace identifier when 128-bit IDs are used.
452    trace_id_high: u64,
453    /// Span identifier for the linked span.
454    span_id: u64,
455    /// Additional attributes attached to the link.
456    attributes: FastHashMap<MetaString, AttributeValue>,
457    /// W3C tracestate value.
458    tracestate: MetaString,
459    /// W3C trace flags where the high bit must be set when provided.
460    flags: u32,
461}
462
463impl SpanLink {
464    /// Creates a new span link for the provided identifiers.
465    pub fn new(trace_id: u64, span_id: u64) -> Self {
466        Self {
467            trace_id,
468            span_id,
469            ..Self::default()
470        }
471    }
472
473    /// Sets the trace identifier.
474    pub fn with_trace_id(mut self, trace_id: u64) -> Self {
475        self.trace_id = trace_id;
476        self
477    }
478
479    /// Sets the high bits of the trace identifier.
480    pub fn with_trace_id_high(mut self, trace_id_high: u64) -> Self {
481        self.trace_id_high = trace_id_high;
482        self
483    }
484
485    /// Sets the span identifier.
486    pub fn with_span_id(mut self, span_id: u64) -> Self {
487        self.span_id = span_id;
488        self
489    }
490
491    /// Replaces the attributes map.
492    pub fn with_attributes(mut self, attributes: impl Into<Option<FastHashMap<MetaString, AttributeValue>>>) -> Self {
493        self.attributes = attributes.into().unwrap_or_default();
494        self
495    }
496
497    /// Sets the W3C tracestate value.
498    pub fn with_tracestate(mut self, tracestate: impl Into<MetaString>) -> Self {
499        self.tracestate = tracestate.into();
500        self
501    }
502
503    /// Sets the W3C trace flags.
504    pub fn with_flags(mut self, flags: u32) -> Self {
505        self.flags = flags;
506        self
507    }
508
509    /// Returns the trace identifier.
510    pub fn trace_id(&self) -> u64 {
511        self.trace_id
512    }
513
514    /// Returns the high bits of the trace identifier.
515    pub fn trace_id_high(&self) -> u64 {
516        self.trace_id_high
517    }
518
519    /// Returns the span identifier.
520    pub fn span_id(&self) -> u64 {
521        self.span_id
522    }
523
524    /// Returns the attributes map.
525    pub fn attributes(&self) -> &FastHashMap<MetaString, AttributeValue> {
526        &self.attributes
527    }
528
529    /// Returns the W3C tracestate value.
530    pub fn tracestate(&self) -> &str {
531        &self.tracestate
532    }
533
534    /// Returns the W3C trace flags.
535    pub fn flags(&self) -> u32 {
536        self.flags
537    }
538}
539
540/// An event associated with a span.
541#[derive(Clone, Debug, PartialEq, Default)]
542pub struct SpanEvent {
543    /// Event timestamp in nanoseconds since Unix epoch.
544    time_unix_nano: u64,
545    /// Event name.
546    name: MetaString,
547    /// Arbitrary attributes describing the event.
548    attributes: FastHashMap<MetaString, AttributeValue>,
549}
550
551impl SpanEvent {
552    /// Creates a new span event with the given timestamp and name.
553    pub fn new(time_unix_nano: u64, name: impl Into<MetaString>) -> Self {
554        Self {
555            time_unix_nano,
556            name: name.into(),
557            ..Self::default()
558        }
559    }
560
561    /// Sets the event timestamp.
562    pub fn with_time_unix_nano(mut self, time_unix_nano: u64) -> Self {
563        self.time_unix_nano = time_unix_nano;
564        self
565    }
566
567    /// Sets the event name.
568    pub fn with_name(mut self, name: impl Into<MetaString>) -> Self {
569        self.name = name.into();
570        self
571    }
572
573    /// Replaces the attributes map.
574    pub fn with_attributes(mut self, attributes: impl Into<Option<FastHashMap<MetaString, AttributeValue>>>) -> Self {
575        self.attributes = attributes.into().unwrap_or_default();
576        self
577    }
578
579    /// Returns the event timestamp.
580    pub fn time_unix_nano(&self) -> u64 {
581        self.time_unix_nano
582    }
583
584    /// Returns the event name.
585    pub fn name(&self) -> &str {
586        &self.name
587    }
588
589    /// Returns the attributes map.
590    pub fn attributes(&self) -> &FastHashMap<MetaString, AttributeValue> {
591        &self.attributes
592    }
593}