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