opentelemetry_spanprocessor_any/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::sdk::trace::SpanLimits;
12use crate::trace::{Event, SpanContext, SpanId, SpanKind, StatusCode};
13use crate::{sdk, trace, KeyValue};
14use std::borrow::Cow;
15use std::sync::Arc;
16use std::time::SystemTime;
17
18/// Single operation within a trace.
19#[derive(Debug)]
20pub struct Span {
21    span_context: SpanContext,
22    data: Option<SpanData>,
23    tracer: sdk::trace::Tracer,
24    span_limits: SpanLimits,
25}
26
27#[derive(Clone, Debug, PartialEq)]
28pub(crate) struct SpanData {
29    /// Span parent id
30    pub(crate) parent_span_id: SpanId,
31    /// Span kind
32    pub(crate) span_kind: SpanKind,
33    /// Span name
34    pub(crate) name: Cow<'static, str>,
35    /// Span start time
36    pub(crate) start_time: SystemTime,
37    /// Span end time
38    pub(crate) end_time: SystemTime,
39    /// Span attributes
40    pub(crate) attributes: sdk::trace::EvictedHashMap,
41    /// Span events
42    pub(crate) events: sdk::trace::EvictedQueue<trace::Event>,
43    /// Span Links
44    pub(crate) links: sdk::trace::EvictedQueue<trace::Link>,
45    /// Span status code
46    pub(crate) status_code: StatusCode,
47    /// Span status message
48    pub(crate) status_message: Cow<'static, str>,
49}
50
51impl Span {
52    pub(crate) fn new(
53        span_context: SpanContext,
54        data: Option<SpanData>,
55        tracer: sdk::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::sdk::export::trace::SpanData> {
78        let (span_context, tracer) = (self.span_context.clone(), &self.tracer);
79        let resource = if let Some(provider) = self.tracer.provider() {
80            provider.config().resource.clone()
81        } else {
82            None
83        };
84        self.data
85            .as_ref()
86            .map(|data| build_export_data(data.clone(), span_context, resource, tracer))
87    }
88}
89
90impl crate::trace::Span for Span {
91    /// Records events at a specific time in the context of a given `Span`.
92    ///
93    /// Note that the OpenTelemetry project documents certain ["standard event names and
94    /// keys"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
95    /// which have prescribed semantic meanings.
96    fn add_event_with_timestamp<T>(
97        &mut self,
98        name: T,
99        timestamp: SystemTime,
100        mut attributes: Vec<KeyValue>,
101    ) where
102        T: Into<Cow<'static, str>>,
103    {
104        let event_attributes_limit = self.span_limits.max_attributes_per_event as usize;
105        self.with_data(|data| {
106            let dropped_attributes_count = attributes.len().saturating_sub(event_attributes_limit);
107            attributes.truncate(event_attributes_limit);
108
109            data.events.push_back(Event::new(
110                name,
111                timestamp,
112                attributes,
113                dropped_attributes_count as u32,
114            ))
115        });
116    }
117
118    /// Returns the `SpanContext` for the given `Span`.
119    fn span_context(&self) -> &SpanContext {
120        &self.span_context
121    }
122
123    /// Returns true if this `Span` is recording information like events with the `add_event`
124    /// operation, attributes using `set_attributes`, status with `set_status`, etc.
125    /// Always returns false after span `end`.
126    fn is_recording(&self) -> bool {
127        self.data.is_some()
128    }
129
130    /// Sets a single `Attribute` where the attribute properties are passed as arguments.
131    ///
132    /// Note that the OpenTelemetry project documents certain ["standard
133    /// attributes"](https://github.com/open-telemetry/opentelemetry-specification/tree/v0.5.0/specification/trace/semantic_conventions/README.md)
134    /// that have prescribed semantic meanings.
135    fn set_attribute(&mut self, attribute: KeyValue) {
136        self.with_data(|data| {
137            data.attributes.insert(attribute);
138        });
139    }
140
141    /// Sets the status of the `Span`. If used, this will override the default `Span`
142    /// status, which is `Unset`. `message` MUST be ignored when the status is `OK` or `Unset`
143    fn set_status(&mut self, code: StatusCode, message: String) {
144        self.with_data(|data| {
145            // check if we should ignore
146            if code.priority() < data.status_code.priority() {
147                // ignore if the current code has higher priority.
148                return;
149            }
150            if code == StatusCode::Error {
151                data.status_message = message.into();
152            }
153            data.status_code = code;
154        });
155    }
156
157    /// Updates the `Span`'s name.
158    fn update_name<T>(&mut self, new_name: T)
159    where
160        T: Into<Cow<'static, str>>,
161    {
162        self.with_data(|data| {
163            data.name = new_name.into();
164        });
165    }
166
167    /// Finishes the span with given timestamp.
168    fn end_with_timestamp(&mut self, timestamp: SystemTime) {
169        self.ensure_ended_and_exported(Some(timestamp));
170    }
171}
172
173impl Span {
174    fn ensure_ended_and_exported(&mut self, timestamp: Option<SystemTime>) {
175        // skip if data has already been exported
176        let mut data = match self.data.take() {
177            Some(data) => data,
178            None => return,
179        };
180
181        // skip if provider has been shut down
182        let provider = match self.tracer.provider() {
183            Some(provider) => provider,
184            None => return,
185        };
186
187        // ensure end time is set via explicit end or implicitly on drop
188        if let Some(timestamp) = timestamp {
189            data.end_time = timestamp;
190        } else if data.end_time == data.start_time {
191            data.end_time = crate::time::now();
192        }
193
194        match provider.span_processors().as_slice() {
195            [] => {}
196            [processor] => {
197                processor.on_end(build_export_data(
198                    data,
199                    self.span_context.clone(),
200                    provider.config().resource.clone(),
201                    &self.tracer,
202                ));
203            }
204            processors => {
205                let config = provider.config();
206                for processor in processors {
207                    processor.on_end(build_export_data(
208                        data.clone(),
209                        self.span_context.clone(),
210                        config.resource.clone(),
211                        &self.tracer,
212                    ));
213                }
214            }
215        }
216    }
217}
218
219impl Drop for Span {
220    /// Report span on inner drop
221    fn drop(&mut self) {
222        self.ensure_ended_and_exported(None);
223    }
224}
225
226fn build_export_data(
227    data: SpanData,
228    span_context: SpanContext,
229    resource: Option<Arc<sdk::Resource>>,
230    tracer: &sdk::trace::Tracer,
231) -> sdk::export::trace::SpanData {
232    sdk::export::trace::SpanData {
233        span_context,
234        parent_span_id: data.parent_span_id,
235        span_kind: data.span_kind,
236        name: data.name,
237        start_time: data.start_time,
238        end_time: data.end_time,
239        attributes: data.attributes,
240        events: data.events,
241        links: data.links,
242        status_code: data.status_code,
243        status_message: data.status_message,
244        resource,
245        instrumentation_lib: tracer.instrumentation_library().clone(),
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use super::*;
252    use crate::sdk::trace::span_limit::{
253        DEFAULT_MAX_ATTRIBUTES_PER_EVENT, DEFAULT_MAX_ATTRIBUTES_PER_LINK,
254    };
255    use crate::trace::{noop::NoopSpanExporter, Link, TraceFlags, TraceId, Tracer};
256    use crate::{trace::Span as _, trace::TracerProvider, KeyValue};
257    use std::time::Duration;
258
259    fn init() -> (sdk::trace::Tracer, SpanData) {
260        let provider = sdk::trace::TracerProvider::default();
261        let config = provider.config();
262        let tracer = provider.tracer("opentelemetry");
263        let data = SpanData {
264            parent_span_id: SpanId::from_u64(0),
265            span_kind: trace::SpanKind::Internal,
266            name: "opentelemetry".into(),
267            start_time: crate::time::now(),
268            end_time: crate::time::now(),
269            attributes: sdk::trace::EvictedHashMap::new(
270                config.span_limits.max_attributes_per_span,
271                0,
272            ),
273            events: sdk::trace::EvictedQueue::new(config.span_limits.max_events_per_span),
274            links: sdk::trace::EvictedQueue::new(config.span_limits.max_links_per_span),
275            status_code: StatusCode::Unset,
276            status_message: "".into(),
277        };
278        (tracer, data)
279    }
280
281    fn create_span() -> Span {
282        let (tracer, data) = init();
283        Span::new(
284            SpanContext::empty_context(),
285            Some(data),
286            tracer,
287            Default::default(),
288        )
289    }
290
291    #[test]
292    fn create_span_without_data() {
293        let (tracer, _) = init();
294        let mut span = Span::new(
295            SpanContext::empty_context(),
296            None,
297            tracer,
298            Default::default(),
299        );
300        span.with_data(|_data| panic!("there are data"));
301    }
302
303    #[test]
304    fn create_span_with_data_mut() {
305        let (tracer, data) = init();
306        let mut span = Span::new(
307            SpanContext::empty_context(),
308            Some(data.clone()),
309            tracer,
310            Default::default(),
311        );
312        span.with_data(|d| assert_eq!(*d, data));
313    }
314
315    #[test]
316    fn add_event() {
317        let mut span = create_span();
318        let name = "some_event".to_string();
319        let attributes = vec![KeyValue::new("k", "v")];
320        span.add_event(name.clone(), attributes.clone());
321        span.with_data(|data| {
322            if let Some(event) = data.events.iter().next() {
323                assert_eq!(event.name, name);
324                assert_eq!(event.attributes, attributes);
325            } else {
326                panic!("no event");
327            }
328        });
329    }
330
331    #[test]
332    fn add_event_with_timestamp() {
333        let mut span = create_span();
334        let name = "some_event".to_string();
335        let attributes = vec![KeyValue::new("k", "v")];
336        let timestamp = crate::time::now();
337        span.add_event_with_timestamp(name.clone(), timestamp, attributes.clone());
338        span.with_data(|data| {
339            if let Some(event) = data.events.iter().next() {
340                assert_eq!(event.timestamp, timestamp);
341                assert_eq!(event.name, name);
342                assert_eq!(event.attributes, attributes);
343            } else {
344                panic!("no event");
345            }
346        });
347    }
348
349    #[test]
350    fn record_exception() {
351        let mut span = create_span();
352        let err = std::io::Error::from(std::io::ErrorKind::Other);
353        span.record_exception(&err);
354        span.with_data(|data| {
355            if let Some(event) = data.events.iter().next() {
356                assert_eq!(event.name, "exception");
357                assert_eq!(
358                    event.attributes,
359                    vec![KeyValue::new("exception.message", err.to_string())]
360                );
361            } else {
362                panic!("no event");
363            }
364        });
365    }
366
367    #[test]
368    fn record_exception_with_stacktrace() {
369        let mut span = create_span();
370        let err = std::io::Error::from(std::io::ErrorKind::Other);
371        let stacktrace = "stacktrace...".to_string();
372        span.record_exception_with_stacktrace(&err, stacktrace.clone());
373        span.with_data(|data| {
374            if let Some(event) = data.events.iter().next() {
375                assert_eq!(event.name, "exception");
376                assert_eq!(
377                    event.attributes,
378                    vec![
379                        KeyValue::new("exception.message", err.to_string()),
380                        KeyValue::new("exception.stacktrace", stacktrace),
381                    ]
382                );
383            } else {
384                panic!("no event");
385            }
386        });
387    }
388
389    #[test]
390    fn set_attribute() {
391        let mut span = create_span();
392        let attributes = KeyValue::new("k", "v");
393        span.set_attribute(attributes.clone());
394        span.with_data(|data| {
395            if let Some(val) = data.attributes.get(&attributes.key) {
396                assert_eq!(*val, attributes.value);
397            } else {
398                panic!("no attribute");
399            }
400        });
401    }
402
403    #[test]
404    fn set_status() {
405        {
406            let mut span = create_span();
407            let status = StatusCode::Ok;
408            let message = "OK".to_string();
409            span.set_status(status, message);
410            span.with_data(|data| {
411                assert_eq!(data.status_code, status);
412                assert_eq!(data.status_message, "");
413            });
414        }
415        {
416            let mut span = create_span();
417            let status = StatusCode::Unset;
418            let message = "OK".to_string();
419            span.set_status(status, message);
420            span.with_data(|data| {
421                assert_eq!(data.status_code, status);
422                assert_eq!(data.status_message, "");
423            });
424        }
425        {
426            let mut span = create_span();
427            let status = StatusCode::Error;
428            let message = "Error".to_string();
429            span.set_status(status, message);
430            span.with_data(|data| {
431                assert_eq!(data.status_code, status);
432                assert_eq!(data.status_message, "Error");
433            });
434        }
435        {
436            let mut span = create_span();
437            // ok status code should be final
438            span.set_status(StatusCode::Ok, "".to_string());
439            span.set_status(StatusCode::Error, "error".to_string());
440            span.with_data(|data| {
441                assert_eq!(data.status_code, StatusCode::Ok);
442                assert_eq!(data.status_message, "");
443            });
444        }
445        {
446            let mut span = create_span();
447            // error status code should be able to override unset
448            span.set_status(StatusCode::Error, "error".to_string());
449            span.with_data(|data| {
450                assert_eq!(data.status_code, StatusCode::Error);
451                assert_eq!(data.status_message, "error");
452            });
453        }
454    }
455
456    #[test]
457    fn update_name() {
458        let mut span = create_span();
459        let name = "new_name".to_string();
460        span.update_name(name.clone());
461        span.with_data(|data| {
462            assert_eq!(data.name, name);
463        });
464    }
465
466    #[test]
467    fn end() {
468        let mut span = create_span();
469        span.end();
470    }
471
472    #[test]
473    fn end_with_timestamp() {
474        let mut span = create_span();
475        let timestamp = crate::time::now();
476        span.end_with_timestamp(timestamp);
477        span.with_data(|data| assert_eq!(data.end_time, timestamp));
478    }
479
480    #[test]
481    fn allows_to_get_span_context_after_end() {
482        let mut span = create_span();
483        span.end();
484        assert_eq!(span.span_context(), &SpanContext::empty_context());
485    }
486
487    #[test]
488    fn end_only_once() {
489        let mut span = create_span();
490        let timestamp = crate::time::now();
491        span.end_with_timestamp(timestamp);
492        span.end_with_timestamp(timestamp.checked_add(Duration::from_secs(10)).unwrap());
493        span.with_data(|data| assert_eq!(data.end_time, timestamp));
494    }
495
496    #[test]
497    fn noop_after_end() {
498        let mut span = create_span();
499        let initial = span.with_data(|data| data.clone()).unwrap();
500        span.end();
501        span.add_event("some_event".to_string(), vec![KeyValue::new("k", "v")]);
502        span.add_event_with_timestamp(
503            "some_event".to_string(),
504            crate::time::now(),
505            vec![KeyValue::new("k", "v")],
506        );
507        let err = std::io::Error::from(std::io::ErrorKind::Other);
508        span.record_exception(&err);
509        span.record_exception_with_stacktrace(&err, "stacktrace...".to_string());
510        span.set_attribute(KeyValue::new("k", "v"));
511        span.set_status(StatusCode::Error, "ERROR".to_string());
512        span.update_name("new_name".to_string());
513        span.with_data(|data| {
514            assert_eq!(data.events, initial.events);
515            assert_eq!(data.attributes, initial.attributes);
516            assert_eq!(data.status_code, initial.status_code);
517            assert_eq!(data.status_message, initial.status_message);
518            assert_eq!(data.name, initial.name);
519        });
520    }
521
522    #[test]
523    fn is_recording_true_when_not_ended() {
524        let span = create_span();
525        assert!(span.is_recording());
526    }
527
528    #[test]
529    fn is_recording_false_after_end() {
530        let mut span = create_span();
531        span.end();
532        assert!(!span.is_recording());
533    }
534
535    #[test]
536    fn exceed_event_attributes_limit() {
537        let exporter = NoopSpanExporter::new();
538        let provider_builder = sdk::trace::TracerProvider::builder().with_simple_exporter(exporter);
539        let provider = provider_builder.build();
540        let tracer = provider.tracer("opentelemetry-test");
541
542        let mut event1 = Event::with_name("test event");
543        for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_EVENT * 2) {
544            event1
545                .attributes
546                .push(KeyValue::new(format!("key {}", i), i.to_string()))
547        }
548        let event2 = event1.clone();
549
550        // add event when build
551        let span_builder = tracer.span_builder("test").with_events(vec![event1]);
552        let mut span = tracer.build(span_builder);
553
554        // add event after build
555        span.add_event("another test event", event2.attributes);
556
557        let event_queue = span
558            .data
559            .clone()
560            .expect("span data should not be empty as we already set it before")
561            .events;
562        let event_vec: Vec<_> = event_queue.iter().take(2).collect();
563        let processed_event_1 = event_vec.get(0).expect("should have at least two events");
564        let processed_event_2 = event_vec.get(1).expect("should have at least two events");
565        assert_eq!(processed_event_1.attributes.len(), 128);
566        assert_eq!(processed_event_2.attributes.len(), 128);
567    }
568
569    #[test]
570    fn exceed_link_attributes_limit() {
571        let exporter = NoopSpanExporter::new();
572        let provider_builder = sdk::trace::TracerProvider::builder().with_simple_exporter(exporter);
573        let provider = provider_builder.build();
574        let tracer = provider.tracer("opentelemetry-test");
575
576        let mut link = Link::new(
577            SpanContext::new(
578                TraceId::from_u128(12),
579                SpanId::from_u64(12),
580                TraceFlags::default(),
581                false,
582                Default::default(),
583            ),
584            Vec::new(),
585        );
586        for i in 0..(DEFAULT_MAX_ATTRIBUTES_PER_LINK * 2) {
587            link.attributes
588                .push(KeyValue::new(format!("key {}", i), i.to_string()));
589        }
590
591        let span_builder = tracer.span_builder("test").with_links(vec![link]);
592        let span = tracer.build(span_builder);
593        let link_queue = span
594            .data
595            .clone()
596            .expect("span data should not be empty as we already set it before")
597            .links;
598        let link_vec: Vec<_> = link_queue.iter().collect();
599        let processed_link = link_vec.get(0).expect("should have at least one link");
600        assert_eq!(processed_link.attributes().len(), 128);
601    }
602
603    #[test]
604    fn test_span_exported_data() {
605        let provider = sdk::trace::TracerProvider::builder()
606            .with_simple_exporter(NoopSpanExporter::new())
607            .build();
608        let tracer = provider.tracer("test");
609
610        let mut span = tracer.start("test_span");
611        span.add_event("test_event".to_string(), vec![]);
612        span.set_status(StatusCode::Error, "".to_string());
613
614        let exported_data = span.exported_data();
615        assert!(exported_data.is_some());
616
617        drop(provider);
618        let dropped_span = tracer.start("span_with_dropped_provider");
619        // return none if the provider has already been dropped
620        assert!(dropped_span.exported_data().is_none());
621    }
622}