opentelemetry_sdk/trace/
span.rs

1//! # Span
2//!
3//! `Span`s represent a single operation within a trace. `Span`s can be nested to form a trace
4//! tree. Each trace contains a root span, which typically describes the end-to-end latency and,
5//! optionally, one or more sub-spans for its sub-operations.
6//!
7//! The `Span`'s start and end timestamps reflect the elapsed real time of the operation. A `Span`'s
8//! start time is set to the current time on span creation. After the `Span` is created, it
9//! is possible to change its name, set its `Attributes`, and add `Links` and `Events`.
10//! These cannot be changed after the `Span`'s end time has been set.
11use crate::trace::SpanLimits;
12use opentelemetry::trace::{Event, Link, SpanContext, SpanId, SpanKind, Status};
13use opentelemetry::KeyValue;
14use std::borrow::Cow;
15use std::time::SystemTime;
16
17/// Single operation within a trace.
18#[derive(Debug)]
19pub struct Span {
20    span_context: SpanContext,
21    data: Option<SpanData>,
22    tracer: crate::trace::SdkTracer,
23    span_limits: SpanLimits,
24}
25
26#[derive(Clone, Debug, PartialEq)]
27pub(crate) struct SpanData {
28    /// Span parent id
29    pub(crate) parent_span_id: SpanId,
30    /// Parent span is remote flag (for span flags)
31    pub(crate) parent_span_is_remote: bool,
32    /// Span kind
33    pub(crate) span_kind: SpanKind,
34    /// Span name
35    pub(crate) name: Cow<'static, str>,
36    /// Span start time
37    pub(crate) start_time: SystemTime,
38    /// Span end time
39    pub(crate) end_time: SystemTime,
40    /// Span attributes
41    pub(crate) attributes: Vec<KeyValue>,
42    /// The number of attributes that were above the configured limit, and thus
43    /// dropped.
44    pub(crate) dropped_attributes_count: u32,
45    /// Span events
46    pub(crate) events: crate::trace::SpanEvents,
47    /// Span Links
48    pub(crate) links: crate::trace::SpanLinks,
49    /// Span status
50    pub(crate) status: Status,
51}
52
53impl Span {
54    pub(crate) fn new(
55        span_context: SpanContext,
56        data: Option<SpanData>,
57        tracer: crate::trace::SdkTracer,
58        span_limit: SpanLimits,
59    ) -> Self {
60        Span {
61            span_context,
62            data,
63            tracer,
64            span_limits: span_limit,
65        }
66    }
67
68    /// Operate on a mutable reference to span data
69    fn with_data<T, F>(&mut self, f: F) -> Option<T>
70    where
71        F: FnOnce(&mut SpanData) -> T,
72    {
73        self.data.as_mut().map(f)
74    }
75
76    /// Convert information in this span into `exporter::trace::SpanData`.
77    /// This function copies all data from the current span, which will create a
78    /// overhead.
79    pub fn exported_data(&self) -> Option<crate::trace::SpanData> {
80        let (span_context, tracer) = (self.span_context.clone(), &self.tracer);
81
82        self.data
83            .as_ref()
84            .map(|data| build_export_data(data.clone(), span_context, tracer))
85    }
86}
87
88impl opentelemetry::trace::Span for Span {
89    /// Records events at a specific time in the context of a given `Span`.
90    ///
91    /// Note that the OpenTelemetry project documents certain ["standard event names and
92    /// keys"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
93    /// which have prescribed semantic meanings.
94    fn add_event_with_timestamp<T>(
95        &mut self,
96        name: T,
97        timestamp: SystemTime,
98        mut attributes: Vec<KeyValue>,
99    ) where
100        T: Into<Cow<'static, str>>,
101    {
102        let span_events_limit = self.span_limits.max_events_per_span as usize;
103        let event_attributes_limit = self.span_limits.max_attributes_per_event as usize;
104        self.with_data(|data| {
105            if data.events.len() < span_events_limit {
106                let dropped_attributes_count =
107                    attributes.len().saturating_sub(event_attributes_limit);
108                attributes.truncate(event_attributes_limit);
109
110                data.events.add_event(Event::new(
111                    name,
112                    timestamp,
113                    attributes,
114                    dropped_attributes_count as u32,
115                ));
116            } else {
117                data.events.dropped_count += 1;
118            }
119        });
120    }
121
122    /// Returns the `SpanContext` for the given `Span`.
123    fn span_context(&self) -> &SpanContext {
124        &self.span_context
125    }
126
127    /// Returns true if this `Span` is recording information like events with the `add_event`
128    /// operation, attributes using `set_attributes`, status with `set_status`, etc.
129    /// Always returns false after span `end`.
130    fn is_recording(&self) -> bool {
131        self.data.is_some()
132    }
133
134    /// Sets a single `Attribute` where the attribute properties are passed as arguments.
135    ///
136    /// Note that the OpenTelemetry project documents certain ["standard
137    /// attributes"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
138    /// that have prescribed semantic meanings.
139    fn set_attribute(&mut self, attribute: KeyValue) {
140        let span_attribute_limit = self.span_limits.max_attributes_per_span as usize;
141        self.with_data(|data| {
142            if data.attributes.len() < span_attribute_limit {
143                data.attributes.push(attribute);
144            } else {
145                data.dropped_attributes_count += 1;
146            }
147        });
148    }
149
150    /// Sets the status of this `Span`.
151    ///
152    /// If used, this will override the default span status, which is [`Status::Unset`].
153    fn set_status(&mut self, status: Status) {
154        self.with_data(|data| {
155            // check if we should update the status
156            // These values form a total order: Ok > Error > Unset.
157            if status > data.status {
158                data.status = status;
159            }
160        });
161    }
162
163    /// Updates the `Span`'s name.
164    fn update_name<T>(&mut self, new_name: T)
165    where
166        T: Into<Cow<'static, str>>,
167    {
168        self.with_data(|data| {
169            data.name = new_name.into();
170        });
171    }
172
173    /// Add `Link` to this `Span`
174    ///
175    fn add_link(&mut self, span_context: SpanContext, attributes: Vec<KeyValue>) {
176        let span_links_limit = self.span_limits.max_links_per_span as usize;
177        let link_attributes_limit = self.span_limits.max_attributes_per_link as usize;
178        self.with_data(|data| {
179            if data.links.links.len() < span_links_limit {
180                let dropped_attributes_count =
181                    attributes.len().saturating_sub(link_attributes_limit);
182                let mut attributes = attributes;
183                attributes.truncate(link_attributes_limit);
184                data.links.add_link(Link::new(
185                    span_context,
186                    attributes,
187                    dropped_attributes_count as u32,
188                ));
189            } else {
190                data.links.dropped_count += 1;
191            }
192        });
193    }
194
195    /// Finishes the span with given timestamp.
196    fn end_with_timestamp(&mut self, timestamp: SystemTime) {
197        self.ensure_ended_and_exported(Some(timestamp));
198    }
199}
200
201impl Span {
202    fn ensure_ended_and_exported(&mut self, timestamp: Option<SystemTime>) {
203        // skip if data has already been exported
204        let mut data = match self.data.take() {
205            Some(data) => data,
206            None => return,
207        };
208
209        let provider = self.tracer.provider();
210        // skip if provider has been shut down
211        if provider.is_shutdown() {
212            return;
213        }
214
215        // ensure end time is set via explicit end or implicitly on drop
216        if let Some(timestamp) = timestamp {
217            data.end_time = timestamp;
218        } else if data.end_time == data.start_time {
219            data.end_time = opentelemetry::time::now();
220        }
221
222        match provider.span_processors() {
223            [] => {}
224            [processor] => {
225                processor.on_end(build_export_data(
226                    data,
227                    self.span_context.clone(),
228                    &self.tracer,
229                ));
230            }
231            processors => {
232                for processor in processors {
233                    processor.on_end(build_export_data(
234                        data.clone(),
235                        self.span_context.clone(),
236                        &self.tracer,
237                    ));
238                }
239            }
240        }
241    }
242}
243
244impl Drop for Span {
245    /// Report span on inner drop
246    fn drop(&mut self) {
247        self.ensure_ended_and_exported(None);
248    }
249}
250
251fn build_export_data(
252    data: SpanData,
253    span_context: SpanContext,
254    tracer: &crate::trace::SdkTracer,
255) -> crate::trace::SpanData {
256    crate::trace::SpanData {
257        span_context,
258        parent_span_id: data.parent_span_id,
259        parent_span_is_remote: data.parent_span_is_remote,
260        span_kind: data.span_kind,
261        name: data.name,
262        start_time: data.start_time,
263        end_time: data.end_time,
264        attributes: data.attributes,
265        dropped_attributes_count: data.dropped_attributes_count,
266        events: data.events,
267        links: data.links,
268        status: data.status,
269        instrumentation_scope: tracer.instrumentation_scope().clone(),
270    }
271}
272
273#[cfg(all(test, feature = "testing"))]
274mod tests {
275    use super::*;
276    use crate::testing::trace::NoopSpanExporter;
277    use crate::trace::span_limit::{
278        DEFAULT_MAX_ATTRIBUTES_PER_EVENT, DEFAULT_MAX_ATTRIBUTES_PER_LINK,
279        DEFAULT_MAX_ATTRIBUTES_PER_SPAN, DEFAULT_MAX_EVENT_PER_SPAN, DEFAULT_MAX_LINKS_PER_SPAN,
280    };
281    use crate::trace::{SpanEvents, SpanLinks};
282    use opentelemetry::trace::{self, SpanBuilder, TraceFlags, TraceId, Tracer};
283    use opentelemetry::{trace::Span as _, trace::TracerProvider};
284    use std::time::Duration;
285    use std::vec;
286
287    fn init() -> (crate::trace::SdkTracer, SpanData) {
288        let provider = crate::trace::SdkTracerProvider::default();
289        let tracer = provider.tracer("opentelemetry");
290        let data = SpanData {
291            parent_span_id: SpanId::from(0),
292            parent_span_is_remote: false,
293            span_kind: trace::SpanKind::Internal,
294            name: "opentelemetry".into(),
295            start_time: opentelemetry::time::now(),
296            end_time: opentelemetry::time::now(),
297            attributes: Vec::new(),
298            dropped_attributes_count: 0,
299            events: SpanEvents::default(),
300            links: SpanLinks::default(),
301            status: Status::Unset,
302        };
303        (tracer, data)
304    }
305
306    fn create_span() -> Span {
307        let (tracer, data) = init();
308        Span::new(
309            SpanContext::empty_context(),
310            Some(data),
311            tracer,
312            Default::default(),
313        )
314    }
315
316    #[test]
317    fn create_span_without_data() {
318        let (tracer, _) = init();
319        let mut span = Span::new(
320            SpanContext::empty_context(),
321            None,
322            tracer,
323            Default::default(),
324        );
325        span.with_data(|_data| panic!("there are data"));
326    }
327
328    #[test]
329    fn create_span_with_data_mut() {
330        let (tracer, data) = init();
331        let mut span = Span::new(
332            SpanContext::empty_context(),
333            Some(data.clone()),
334            tracer,
335            Default::default(),
336        );
337        span.with_data(|d| assert_eq!(*d, data));
338    }
339
340    #[test]
341    fn add_event() {
342        let mut span = create_span();
343        let name = "some_event";
344        let attributes = vec![KeyValue::new("k", "v")];
345        span.add_event(name, attributes.clone());
346        span.with_data(|data| {
347            if let Some(event) = data.events.iter().next() {
348                assert_eq!(event.name, name);
349                assert_eq!(event.attributes, attributes);
350            } else {
351                panic!("no event");
352            }
353        });
354    }
355
356    #[test]
357    fn add_event_with_timestamp() {
358        let mut span = create_span();
359        let name = "some_event";
360        let attributes = vec![KeyValue::new("k", "v")];
361        let timestamp = opentelemetry::time::now();
362        span.add_event_with_timestamp(name, timestamp, attributes.clone());
363        span.with_data(|data| {
364            if let Some(event) = data.events.iter().next() {
365                assert_eq!(event.timestamp, timestamp);
366                assert_eq!(event.name, name);
367                assert_eq!(event.attributes, attributes);
368            } else {
369                panic!("no event");
370            }
371        });
372    }
373
374    #[test]
375    fn record_error() {
376        let mut span = create_span();
377        let err = std::io::Error::from(std::io::ErrorKind::Other);
378        span.record_error(&err);
379        span.with_data(|data| {
380            if let Some(event) = data.events.iter().next() {
381                assert_eq!(event.name, "exception");
382                assert_eq!(
383                    event.attributes,
384                    vec![KeyValue::new("exception.message", err.to_string())]
385                );
386            } else {
387                panic!("no event");
388            }
389        });
390    }
391
392    #[test]
393    fn set_attribute() {
394        let mut span = create_span();
395        let attributes = KeyValue::new("k", "v");
396        span.set_attribute(attributes.clone());
397        span.with_data(|data| {
398            let matching_attribute: Vec<&KeyValue> = data
399                .attributes
400                .iter()
401                .filter(|kv| kv.key.as_str() == attributes.key.as_str())
402                .collect();
403            if matching_attribute.len() == 1 {
404                assert_eq!(matching_attribute[0].value, attributes.value);
405            } else {
406                panic!("no attribute");
407            }
408        });
409    }
410
411    #[test]
412    fn set_attributes() {
413        let mut span = create_span();
414        let attributes = vec![KeyValue::new("k1", "v1"), KeyValue::new("k2", "v2")];
415        span.set_attributes(attributes);
416        span.with_data(|data| {
417            assert_eq!(data.attributes.len(), 2);
418        });
419    }
420
421    #[test]
422    fn set_status() {
423        {
424            let mut span = create_span();
425            let status = Status::Ok;
426            span.set_status(status.clone());
427            span.with_data(|data| assert_eq!(data.status, status));
428        }
429        {
430            let mut span = create_span();
431            let status = Status::Unset;
432            span.set_status(status.clone());
433            span.with_data(|data| assert_eq!(data.status, status));
434        }
435        {
436            let mut span = create_span();
437            let status = Status::error("error");
438            span.set_status(status.clone());
439            span.with_data(|data| assert_eq!(data.status, status));
440        }
441        {
442            let mut span = create_span();
443            // ok status should be final
444            span.set_status(Status::Ok);
445            span.set_status(Status::error("error"));
446            span.with_data(|data| assert_eq!(data.status, Status::Ok));
447        }
448        {
449            let mut span = create_span();
450            // error status should be able to override unset
451            span.set_status(Status::Unset);
452            span.set_status(Status::error("error"));
453            span.with_data(|data| assert_ne!(data.status, Status::Ok));
454        }
455    }
456
457    #[test]
458    fn update_name() {
459        let mut span = create_span();
460        let name = "new_name";
461        span.update_name(name);
462        span.with_data(|data| {
463            assert_eq!(data.name, name);
464        });
465    }
466
467    #[test]
468    fn end() {
469        let mut span = create_span();
470        span.end();
471    }
472
473    #[test]
474    fn end_with_timestamp() {
475        let mut span = create_span();
476        let timestamp = opentelemetry::time::now();
477        span.end_with_timestamp(timestamp);
478        span.with_data(|data| assert_eq!(data.end_time, timestamp));
479    }
480
481    #[test]
482    fn allows_to_get_span_context_after_end() {
483        let mut span = create_span();
484        span.end();
485        assert_eq!(span.span_context(), &SpanContext::empty_context());
486    }
487
488    #[test]
489    fn end_only_once() {
490        let mut span = create_span();
491        let timestamp = opentelemetry::time::now();
492        span.end_with_timestamp(timestamp);
493        span.end_with_timestamp(timestamp.checked_add(Duration::from_secs(10)).unwrap());
494        span.with_data(|data| assert_eq!(data.end_time, timestamp));
495    }
496
497    #[test]
498    fn noop_after_end() {
499        let mut span = create_span();
500        let initial = span.with_data(|data| data.clone()).unwrap();
501        span.end();
502        span.add_event("some_event", vec![KeyValue::new("k", "v")]);
503        span.add_event_with_timestamp(
504            "some_event",
505            opentelemetry::time::now(),
506            vec![KeyValue::new("k", "v")],
507        );
508        let err = std::io::Error::from(std::io::ErrorKind::Other);
509        span.record_error(&err);
510        span.set_attribute(KeyValue::new("k", "v"));
511        span.set_status(Status::error("ERROR"));
512        span.update_name("new_name");
513        span.with_data(|data| {
514            assert_eq!(data.events, initial.events);
515            assert_eq!(data.attributes, initial.attributes);
516            assert_eq!(data.status, initial.status);
517            assert_eq!(data.name, initial.name);
518        });
519    }
520
521    #[test]
522    fn is_recording_true_when_not_ended() {
523        let span = create_span();
524        assert!(span.is_recording());
525    }
526
527    #[test]
528    fn is_recording_false_after_end() {
529        let mut span = create_span();
530        span.end();
531        assert!(!span.is_recording());
532    }
533
534    #[test]
535    fn exceed_span_attributes_limit() {
536        let exporter = NoopSpanExporter::new();
537        let provider_builder =
538            crate::trace::SdkTracerProvider::builder().with_simple_exporter(exporter);
539        let provider = provider_builder.build();
540        let tracer = provider.tracer("opentelemetry-test");
541
542        let mut initial_attributes = Vec::new();
543        let mut expected_dropped_count = 1;
544        for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_SPAN + 1) {
545            initial_attributes.push(KeyValue::new(format!("key {i}"), i.to_string()))
546        }
547        let span_builder = SpanBuilder::from_name("test_span").with_attributes(initial_attributes);
548
549        let mut span = tracer.build(span_builder);
550        expected_dropped_count += 1;
551        span.set_attribute(KeyValue::new("key3", "value3"));
552
553        expected_dropped_count += 2;
554        let span_attributes_after_creation =
555            vec![KeyValue::new("foo", "1"), KeyValue::new("bar", "2")];
556        span.set_attributes(span_attributes_after_creation);
557
558        let actual_span = span
559            .data
560            .clone()
561            .expect("span data should not be empty as we already set it before");
562        assert_eq!(
563            actual_span.attributes.len(),
564            DEFAULT_MAX_ATTRIBUTES_PER_SPAN as usize,
565            "Span attributes should be truncated to the max limit"
566        );
567        assert_eq!(
568            actual_span.dropped_attributes_count, expected_dropped_count,
569            "Dropped count should match the actual count of attributes dropped"
570        );
571    }
572
573    #[test]
574    fn exceed_event_attributes_limit() {
575        let exporter = NoopSpanExporter::new();
576        let provider_builder =
577            crate::trace::SdkTracerProvider::builder().with_simple_exporter(exporter);
578        let provider = provider_builder.build();
579        let tracer = provider.tracer("opentelemetry-test");
580
581        let mut event1 = Event::with_name("test event");
582        for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_EVENT * 2) {
583            event1
584                .attributes
585                .push(KeyValue::new(format!("key {i}"), i.to_string()))
586        }
587        let event2 = event1.clone();
588
589        // add event when build
590        let span_builder = tracer.span_builder("test").with_events(vec![event1]);
591        let mut span = tracer.build(span_builder);
592
593        // add event after build
594        span.add_event("another test event", event2.attributes);
595
596        let event_queue = span
597            .data
598            .clone()
599            .expect("span data should not be empty as we already set it before")
600            .events;
601        let event_vec: Vec<_> = event_queue.iter().take(2).collect();
602        #[allow(clippy::get_first)] // we want to extract first two elements
603        let processed_event_1 = event_vec.get(0).expect("should have at least two events");
604        let processed_event_2 = event_vec.get(1).expect("should have at least two events");
605        assert_eq!(processed_event_1.attributes.len(), 128);
606        assert_eq!(processed_event_2.attributes.len(), 128);
607    }
608
609    #[test]
610    fn exceed_link_attributes_limit() {
611        let exporter = NoopSpanExporter::new();
612        let provider_builder =
613            crate::trace::SdkTracerProvider::builder().with_simple_exporter(exporter);
614        let provider = provider_builder.build();
615        let tracer = provider.tracer("opentelemetry-test");
616
617        let mut link = Link::with_context(SpanContext::new(
618            TraceId::from(12),
619            SpanId::from(12),
620            TraceFlags::default(),
621            false,
622            Default::default(),
623        ));
624        for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_LINK * 2) {
625            link.attributes
626                .push(KeyValue::new(format!("key {i}"), i.to_string()));
627        }
628
629        let span_builder = tracer.span_builder("test").with_links(vec![link]);
630        let span = tracer.build(span_builder);
631        let link_queue = span
632            .data
633            .clone()
634            .expect("span data should not be empty as we already set it before")
635            .links;
636        let link_vec: Vec<_> = link_queue.links;
637        let processed_link = link_vec.first().expect("should have at least one link");
638        assert_eq!(processed_link.attributes.len(), 128);
639    }
640
641    #[test]
642    fn exceed_span_links_limit() {
643        let exporter = NoopSpanExporter::new();
644        let provider_builder =
645            crate::trace::SdkTracerProvider::builder().with_simple_exporter(exporter);
646        let provider = provider_builder.build();
647        let tracer = provider.tracer("opentelemetry-test");
648
649        let mut links = Vec::new();
650        for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) {
651            links.push(Link::with_context(SpanContext::new(
652                TraceId::from(12),
653                SpanId::from(12),
654                TraceFlags::default(),
655                false,
656                Default::default(),
657            )))
658        }
659
660        let span_builder = tracer.span_builder("test").with_links(links);
661        let mut span = tracer.build(span_builder);
662
663        // add links using span api after building the span
664        span.add_link(
665            SpanContext::new(
666                TraceId::from(12),
667                SpanId::from(12),
668                TraceFlags::default(),
669                false,
670                Default::default(),
671            ),
672            vec![],
673        );
674        let link_queue = span
675            .data
676            .clone()
677            .expect("span data should not be empty as we already set it before")
678            .links;
679        let link_vec: Vec<_> = link_queue.links;
680        assert_eq!(link_vec.len(), DEFAULT_MAX_LINKS_PER_SPAN as usize);
681    }
682
683    #[test]
684    fn exceed_span_events_limit() {
685        let exporter = NoopSpanExporter::new();
686        let provider_builder =
687            crate::trace::SdkTracerProvider::builder().with_simple_exporter(exporter);
688        let provider = provider_builder.build();
689        let tracer = provider.tracer("opentelemetry-test");
690
691        let mut events = Vec::new();
692        for _i in 0..(DEFAULT_MAX_EVENT_PER_SPAN * 2) {
693            events.push(Event::with_name("test event"))
694        }
695
696        // add events via span builder
697        let span_builder = tracer.span_builder("test").with_events(events);
698        let mut span = tracer.build(span_builder);
699
700        // add events using span api after building the span
701        span.add_event("test event again, after span builder", Vec::new());
702        span.add_event("test event once again, after span builder", Vec::new());
703        let span_events = span
704            .data
705            .clone()
706            .expect("span data should not be empty as we already set it before")
707            .events;
708        let event_vec: Vec<_> = span_events.events;
709        assert_eq!(event_vec.len(), DEFAULT_MAX_EVENT_PER_SPAN as usize);
710    }
711
712    #[test]
713    fn test_span_exported_data() {
714        let provider = crate::trace::SdkTracerProvider::builder()
715            .with_simple_exporter(NoopSpanExporter::new())
716            .build();
717        let tracer = provider.tracer("test");
718
719        let mut span = tracer.start("test_span");
720        span.add_event("test_event", vec![]);
721        span.set_status(Status::error(""));
722
723        let exported_data = span.exported_data();
724        assert!(exported_data.is_some());
725        let res = provider.shutdown();
726        println!("{res:?}");
727        assert!(res.is_ok());
728        let dropped_span = tracer.start("span_with_dropped_provider");
729        // return none if the provider has already been dropped
730        assert!(dropped_span.exported_data().is_none());
731    }
732}