opentelemetry_sdk/trace/
mod.rs

1//! # OpenTelemetry Trace SDK
2//!
3//! The tracing SDK consist of a few main structs:
4//!
5//! * The [`SdkTracer`] struct which performs all tracing operations.
6//! * The [`Span`] struct with is a mutable object storing information about the
7//!   current operation execution.
8//! * The [`SdkTracerProvider`] struct which configures and produces [`SdkTracer`]s.
9mod config;
10mod error;
11mod events;
12mod export;
13mod id_generator;
14mod links;
15mod provider;
16mod sampler;
17mod span;
18mod span_limit;
19mod span_processor;
20#[cfg(feature = "experimental_trace_batch_span_processor_with_async_runtime")]
21/// Experimental feature to use async runtime with batch span processor.
22pub mod span_processor_with_async_runtime;
23mod tracer;
24
25pub use config::Config;
26pub use error::{TraceError, TraceResult};
27pub use events::SpanEvents;
28pub use export::{SpanData, SpanExporter};
29
30/// In-Memory span exporter for testing purpose.
31#[cfg(any(feature = "testing", test))]
32#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
33pub mod in_memory_exporter;
34#[cfg(any(feature = "testing", test))]
35#[cfg_attr(docsrs, doc(cfg(any(feature = "testing", test))))]
36pub use in_memory_exporter::{InMemorySpanExporter, InMemorySpanExporterBuilder};
37
38pub use id_generator::{IdGenerator, RandomIdGenerator};
39pub use links::SpanLinks;
40pub use provider::{SdkTracerProvider, TracerProviderBuilder};
41pub use sampler::{Sampler, ShouldSample};
42pub use span::Span;
43pub use span_limit::SpanLimits;
44pub use span_processor::{
45    BatchConfig, BatchConfigBuilder, BatchSpanProcessor, BatchSpanProcessorBuilder,
46    SimpleSpanProcessor, SpanProcessor,
47};
48
49pub use tracer::SdkTracer;
50pub use tracer::SdkTracer as Tracer; // for back-compat else tracing-opentelemetry won't build
51
52#[cfg(feature = "jaeger_remote_sampler")]
53pub use sampler::{JaegerRemoteSampler, JaegerRemoteSamplerBuilder};
54
55#[cfg(feature = "experimental_trace_batch_span_processor_with_async_runtime")]
56#[cfg(test)]
57mod runtime_tests;
58
59#[cfg(all(test, feature = "testing"))]
60mod tests {
61    use super::*;
62    use crate::error::OTelSdkResult;
63    use crate::{
64        trace::span_limit::{DEFAULT_MAX_EVENT_PER_SPAN, DEFAULT_MAX_LINKS_PER_SPAN},
65        trace::{InMemorySpanExporter, InMemorySpanExporterBuilder},
66    };
67    use opentelemetry::{
68        baggage::BaggageExt,
69        trace::{SamplingDecision, SamplingResult, SpanKind, Status, TraceContextExt, TraceState},
70    };
71    use opentelemetry::{testing::trace::TestSpan, InstrumentationScope};
72    use opentelemetry::{
73        trace::{
74            Event, Link, Span, SpanBuilder, SpanContext, SpanId, TraceFlags, TraceId, Tracer,
75            TracerProvider,
76        },
77        Context, KeyValue,
78    };
79    use std::time::Duration;
80
81    #[test]
82    fn span_modification_via_context() {
83        let exporter = InMemorySpanExporterBuilder::new().build();
84        let provider = SdkTracerProvider::builder()
85            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
86            .build();
87        let tracer = provider.tracer("test_tracer");
88
89        #[derive(Debug, PartialEq)]
90        struct ValueA(u64);
91
92        let span = tracer.start("span-name");
93
94        // start with Current, which should have no span
95        let cx = Context::current();
96        assert!(!cx.has_active_span());
97
98        // add span to context
99        let cx_with_span = cx.with_span(span);
100        assert!(cx_with_span.has_active_span());
101        assert!(!cx.has_active_span());
102
103        // modify the span by using span_ref from the context
104        // this is the only way to modify the span as span
105        // is moved to context.
106        let span_ref = cx_with_span.span();
107        span_ref.set_attribute(KeyValue::new("attribute1", "value1"));
108
109        // create a new context, which should not affect the original
110        let cx_with_span_and_more = cx_with_span.with_value(ValueA(1));
111
112        // modify the span again using the new context.
113        // this should still be using the original span itself.
114        let span_ref_new = cx_with_span_and_more.span();
115        span_ref_new.set_attribute(KeyValue::new("attribute2", "value2"));
116
117        span_ref_new.end();
118
119        let exported_spans = exporter
120            .get_finished_spans()
121            .expect("Spans are expected to be exported.");
122        // There should be a single span, with attributes from both modifications.
123        assert_eq!(exported_spans.len(), 1);
124        let span = &exported_spans[0];
125        assert_eq!(span.attributes.len(), 2);
126    }
127
128    #[derive(Debug)]
129    struct BaggageInspectingSpanProcessor;
130    impl SpanProcessor for BaggageInspectingSpanProcessor {
131        fn on_start(&self, span: &mut crate::trace::Span, cx: &Context) {
132            let baggage = cx.baggage();
133            if let Some(baggage_value) = baggage.get("bag-key") {
134                span.set_attribute(KeyValue::new("bag-key", baggage_value.to_string()));
135            } else {
136                unreachable!("Baggage should be present in the context");
137            }
138        }
139
140        fn on_end(&self, _span: SpanData) {
141            // TODO: Accessing Context::current() will panic today and hence commented out.
142            // See https://github.com/open-telemetry/opentelemetry-rust/issues/2871
143            // let _c = Context::current();
144        }
145
146        fn force_flush(&self) -> crate::error::OTelSdkResult {
147            Ok(())
148        }
149
150        fn shutdown_with_timeout(&self, _timeout: Duration) -> OTelSdkResult {
151            Ok(())
152        }
153    }
154
155    #[test]
156    fn span_and_baggage() {
157        let provider = SdkTracerProvider::builder()
158            .with_span_processor(BaggageInspectingSpanProcessor)
159            .build();
160
161        let cx_with_baggage =
162            Context::current_with_baggage(vec![KeyValue::new("bag-key", "bag-value")]);
163
164        // assert baggage is in the context
165        assert_eq!(
166            cx_with_baggage
167                .baggage()
168                .get("bag-key")
169                .unwrap()
170                .to_string(),
171            "bag-value"
172        );
173
174        // Attach context to current
175        let _cx_guard1 = cx_with_baggage.attach();
176        // now Current should have the baggage
177        assert_eq!(
178            Context::current()
179                .baggage()
180                .get("bag-key")
181                .unwrap()
182                .to_string(),
183            "bag-value"
184        );
185
186        let tracer = provider.tracer("test_tracer");
187        let mut span = tracer
188            .span_builder("span-name")
189            .start_with_context(&tracer, &Context::current());
190        span.set_attribute(KeyValue::new("attribute1", "value1"));
191
192        // We have not added span to the context yet
193        // so the current context should not have any span.
194        let cx = Context::current();
195        assert!(!cx.has_active_span());
196
197        // Now add span to context which already has baggage.
198        let cx_with_baggage_and_span = cx.with_span(span);
199        assert!(cx_with_baggage_and_span.has_active_span());
200        assert_eq!(
201            cx_with_baggage_and_span
202                .baggage()
203                .get("bag-key")
204                .unwrap()
205                .to_string(),
206            "bag-value"
207        );
208
209        let _cx_guard2 = cx_with_baggage_and_span.attach();
210        // Now current context should have both baggage and span.
211        assert!(Context::current().has_active_span());
212        assert_eq!(
213            Context::current()
214                .baggage()
215                .get("bag-key")
216                .unwrap()
217                .to_string(),
218            "bag-value"
219        );
220    }
221
222    #[test]
223    fn tracer_in_span() {
224        // Arrange
225        let exporter = InMemorySpanExporterBuilder::new().build();
226        let provider = SdkTracerProvider::builder()
227            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
228            .build();
229
230        // Act
231        let tracer = provider.tracer("test_tracer");
232        tracer.in_span("span_name", |cx| {
233            let span = cx.span();
234            assert!(span.is_recording());
235            span.update_name("span_name_updated");
236            span.set_attribute(KeyValue::new("attribute1", "value1"));
237            span.add_event("test-event".to_string(), vec![]);
238            span.add_link(
239                SpanContext::new(
240                    TraceId::from(47),
241                    SpanId::from(11),
242                    TraceFlags::default(),
243                    false,
244                    Default::default(),
245                ),
246                vec![],
247            );
248        });
249
250        // Assert
251        let exported_spans = exporter
252            .get_finished_spans()
253            .expect("Spans are expected to be exported.");
254        assert_eq!(exported_spans.len(), 1);
255        let span = &exported_spans[0];
256        assert_eq!(span.name, "span_name_updated");
257        assert_eq!(span.instrumentation_scope.name(), "test_tracer");
258        assert_eq!(span.attributes.len(), 1);
259        assert_eq!(span.events.len(), 1);
260        assert_eq!(span.events[0].name, "test-event");
261        assert_eq!(span.links.len(), 1);
262        assert_eq!(span.links[0].span_context.trace_id(), TraceId::from(47));
263        assert_eq!(span.links[0].span_context.span_id(), SpanId::from(11));
264        assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
265        assert!(!span.span_context.is_remote());
266        assert_eq!(span.status, Status::Unset);
267    }
268
269    #[test]
270    fn tracer_start() {
271        // Arrange
272        let exporter = InMemorySpanExporterBuilder::new().build();
273        let provider = SdkTracerProvider::builder()
274            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
275            .build();
276
277        // Act
278        let tracer = provider.tracer("test_tracer");
279        let mut span = tracer.start("span_name");
280        span.set_attribute(KeyValue::new("attribute1", "value1"));
281        span.add_event("test-event".to_string(), vec![]);
282        span.set_status(Status::error("cancelled"));
283        span.end();
284
285        // After span end, further operations should not have any effect
286        span.update_name("span_name_updated");
287
288        // Assert
289        let exported_spans = exporter
290            .get_finished_spans()
291            .expect("Spans are expected to be exported.");
292        assert_eq!(exported_spans.len(), 1);
293        let span = &exported_spans[0];
294        assert_eq!(span.name, "span_name");
295        assert_eq!(span.instrumentation_scope.name(), "test_tracer");
296        assert_eq!(span.attributes.len(), 1);
297        assert_eq!(span.events.len(), 1);
298        assert_eq!(span.events[0].name, "test-event");
299        assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
300        assert!(!span.span_context.is_remote());
301        let status_expected = Status::error("cancelled");
302        assert_eq!(span.status, status_expected);
303    }
304
305    #[test]
306    fn tracer_span_builder() {
307        // Arrange
308        let exporter = InMemorySpanExporterBuilder::new().build();
309        let provider = SdkTracerProvider::builder()
310            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
311            .build();
312
313        // Act
314        let tracer = provider.tracer("test_tracer");
315        let mut span = tracer
316            .span_builder("span_name")
317            .with_kind(SpanKind::Server)
318            .start(&tracer);
319        span.set_attribute(KeyValue::new("attribute1", "value1"));
320        span.add_event("test-event".to_string(), vec![]);
321        span.set_status(Status::Ok);
322        drop(span);
323
324        // Assert
325        let exported_spans = exporter
326            .get_finished_spans()
327            .expect("Spans are expected to be exported.");
328        assert_eq!(exported_spans.len(), 1);
329        let span = &exported_spans[0];
330        assert_eq!(span.name, "span_name");
331        assert_eq!(span.span_kind, SpanKind::Server);
332        assert_eq!(span.instrumentation_scope.name(), "test_tracer");
333        assert_eq!(span.attributes.len(), 1);
334        assert_eq!(span.events.len(), 1);
335        assert_eq!(span.events[0].name, "test-event");
336        assert_eq!(span.span_context.trace_flags(), TraceFlags::SAMPLED);
337        assert!(!span.span_context.is_remote());
338        assert_eq!(span.status, Status::Ok);
339    }
340
341    #[test]
342    fn exceed_span_links_limit() {
343        // Arrange
344        let exporter = InMemorySpanExporterBuilder::new().build();
345        let provider = SdkTracerProvider::builder()
346            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
347            .build();
348
349        // Act
350        let tracer = provider.tracer("test_tracer");
351
352        let mut links = Vec::new();
353        for _i in 0..(DEFAULT_MAX_LINKS_PER_SPAN * 2) {
354            links.push(Link::with_context(SpanContext::new(
355                TraceId::from(12),
356                SpanId::from(12),
357                TraceFlags::default(),
358                false,
359                Default::default(),
360            )))
361        }
362
363        let span_builder = SpanBuilder::from_name("span_name").with_links(links);
364        let mut span = tracer.build(span_builder);
365        span.end();
366
367        // Assert
368        let exported_spans = exporter
369            .get_finished_spans()
370            .expect("Spans are expected to be exported.");
371        assert_eq!(exported_spans.len(), 1);
372        let span = &exported_spans[0];
373        assert_eq!(span.name, "span_name");
374        assert_eq!(span.links.len(), DEFAULT_MAX_LINKS_PER_SPAN as usize);
375    }
376
377    #[test]
378    fn exceed_span_events_limit() {
379        // Arrange
380        let exporter = InMemorySpanExporterBuilder::new().build();
381        let provider = SdkTracerProvider::builder()
382            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
383            .build();
384
385        // Act
386        let tracer = provider.tracer("test_tracer");
387
388        let mut events = Vec::new();
389        for _i in 0..(DEFAULT_MAX_EVENT_PER_SPAN * 2) {
390            events.push(Event::with_name("test event"))
391        }
392
393        // add events via span builder
394        let span_builder = SpanBuilder::from_name("span_name").with_events(events);
395        let mut span = tracer.build(span_builder);
396
397        // add events using span api after building the span
398        span.add_event("test event again, after span builder", Vec::new());
399        span.add_event("test event once again, after span builder", Vec::new());
400        span.end();
401
402        // Assert
403        let exported_spans = exporter
404            .get_finished_spans()
405            .expect("Spans are expected to be exported.");
406        assert_eq!(exported_spans.len(), 1);
407        let span = &exported_spans[0];
408        assert_eq!(span.name, "span_name");
409        assert_eq!(span.events.len(), DEFAULT_MAX_EVENT_PER_SPAN as usize);
410        assert_eq!(span.events.dropped_count, DEFAULT_MAX_EVENT_PER_SPAN + 2);
411    }
412
413    #[test]
414    fn trace_state_for_dropped_sampler() {
415        let exporter = InMemorySpanExporterBuilder::new().build();
416        let provider = SdkTracerProvider::builder()
417            .with_sampler(Sampler::AlwaysOff)
418            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
419            .build();
420
421        let tracer = provider.tracer("test");
422        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
423
424        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
425            TraceId::from(10000),
426            SpanId::from(20),
427            TraceFlags::SAMPLED,
428            true,
429            trace_state.clone(),
430        )));
431
432        let span = tracer.start_with_context("span", &parent_context);
433        assert_eq!(
434            span.span_context().trace_state().get("foo"),
435            trace_state.get("foo")
436        )
437    }
438
439    #[derive(Clone, Debug, Default)]
440    struct TestRecordOnlySampler {}
441
442    impl ShouldSample for TestRecordOnlySampler {
443        fn should_sample(
444            &self,
445            parent_context: Option<&Context>,
446            _trace_id: TraceId,
447            _name: &str,
448            _span_kind: &SpanKind,
449            _attributes: &[KeyValue],
450            _links: &[Link],
451        ) -> SamplingResult {
452            let trace_state = parent_context
453                .unwrap()
454                .span()
455                .span_context()
456                .trace_state()
457                .clone();
458            SamplingResult {
459                decision: SamplingDecision::RecordOnly,
460                attributes: vec![KeyValue::new("record_only_key", "record_only_value")],
461                trace_state,
462            }
463        }
464    }
465
466    #[test]
467    fn trace_state_for_record_only_sampler() {
468        let exporter = InMemorySpanExporterBuilder::new().build();
469        let provider = SdkTracerProvider::builder()
470            .with_sampler(TestRecordOnlySampler::default())
471            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
472            .build();
473
474        let tracer = provider.tracer("test");
475        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
476
477        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
478            TraceId::from(10000),
479            SpanId::from(20),
480            TraceFlags::SAMPLED,
481            true,
482            trace_state.clone(),
483        )));
484
485        let span = tracer.build_with_context(
486            SpanBuilder::from_name("span")
487                .with_attributes(vec![KeyValue::new("extra_attr_key", "extra_attr_value")]),
488            &parent_context,
489        );
490        assert!(!span.span_context().trace_flags().is_sampled());
491        assert_eq!(
492            span.exported_data().unwrap().attributes,
493            vec![
494                KeyValue::new("extra_attr_key", "extra_attr_value"),
495                KeyValue::new("record_only_key", "record_only_value")
496            ]
497        );
498        assert_eq!(span.span_context().trace_state().get("foo"), Some("bar"));
499    }
500
501    #[test]
502    fn tracer_attributes() {
503        let provider = SdkTracerProvider::builder().build();
504        let scope = InstrumentationScope::builder("basic")
505            .with_attributes(vec![KeyValue::new("test_k", "test_v")])
506            .build();
507
508        let tracer = provider.tracer_with_scope(scope);
509        let instrumentation_scope = tracer.instrumentation_scope();
510        assert!(instrumentation_scope
511            .attributes()
512            .eq(&[KeyValue::new("test_k", "test_v")]));
513    }
514
515    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
516    async fn empty_tracer_name_retained() {
517        async fn tracer_name_retained_helper(
518            tracer: super::SdkTracer,
519            provider: SdkTracerProvider,
520            exporter: InMemorySpanExporter,
521        ) {
522            // Act
523            tracer.start("my_span").end();
524
525            // Force flush to ensure spans are exported
526            assert!(provider.force_flush().is_ok());
527
528            // Assert
529            let finished_spans = exporter
530                .get_finished_spans()
531                .expect("spans are expected to be exported.");
532            assert_eq!(finished_spans.len(), 1, "There should be a single span");
533
534            let tracer_name = finished_spans[0].instrumentation_scope.name();
535            assert_eq!(tracer_name, "", "The tracer name should be an empty string");
536
537            exporter.reset();
538        }
539
540        let exporter = InMemorySpanExporter::default();
541        let span_processor = SimpleSpanProcessor::new(exporter.clone());
542        let tracer_provider = SdkTracerProvider::builder()
543            .with_span_processor(span_processor)
544            .build();
545
546        // Test Tracer creation in 2 ways, both with empty string as tracer name
547        let tracer1 = tracer_provider.tracer("");
548        tracer_name_retained_helper(tracer1, tracer_provider.clone(), exporter.clone()).await;
549
550        let tracer_scope = InstrumentationScope::builder("").build();
551        let tracer2 = tracer_provider.tracer_with_scope(tracer_scope);
552        tracer_name_retained_helper(tracer2, tracer_provider, exporter).await;
553    }
554
555    #[test]
556    fn trace_suppression() {
557        // Arrange
558        let exporter = InMemorySpanExporter::default();
559        let span_processor = SimpleSpanProcessor::new(exporter.clone());
560        let tracer_provider = SdkTracerProvider::builder()
561            .with_span_processor(span_processor)
562            .build();
563
564        // Act
565        let tracer = tracer_provider.tracer("test");
566        {
567            let _suppressed_context = Context::enter_telemetry_suppressed_scope();
568            // This span should not be emitted as it is created in a suppressed context
569            let _span = tracer.span_builder("span_name").start(&tracer);
570        }
571
572        // Assert
573        let finished_spans = exporter.get_finished_spans().expect("this should not fail");
574        assert_eq!(
575            finished_spans.len(),
576            0,
577            "There should be a no spans as span emission is done inside a suppressed context"
578        );
579    }
580}