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