Skip to main content

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