Skip to main content

opentelemetry_sdk/trace/
tracer.rs

1//! # Tracer
2//!
3//! The OpenTelemetry library achieves in-process context propagation of
4//! `Span`s by way of the `Tracer`.
5//!
6//! The `Tracer` is responsible for tracking the currently active `Span`,
7//! and exposes methods for creating and activating new `Spans`.
8//!
9//! Docs: <https://github.com/open-telemetry/opentelemetry-specification/blob/v1.3.0/specification/trace/api.md#tracer>
10use crate::trace::{
11    provider::SdkTracerProvider,
12    span::{Span, SpanData},
13    SamplingDecision, SpanEvents, SpanLimits, SpanLinks,
14};
15use opentelemetry::{
16    trace::{Span as _, SpanBuilder, SpanContext, SpanKind, Status, TraceContextExt, TraceFlags},
17    Context, InstrumentationScope, KeyValue,
18};
19use std::fmt;
20use std::sync::Arc;
21
22/// `Tracer` implementation to create and manage spans
23#[derive(Clone)]
24pub struct SdkTracer {
25    scope: Arc<InstrumentationScope>,
26    provider: SdkTracerProvider,
27}
28
29impl fmt::Debug for SdkTracer {
30    /// Formats the `Tracer` using the given formatter.
31    /// Omitting `provider` here is necessary to avoid cycles.
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        f.debug_struct("Tracer")
34            .field("name", &self.scope.name())
35            .field("version", &self.scope.version())
36            .finish()
37    }
38}
39
40impl SdkTracer {
41    /// Create a new tracer (used internally by `TracerProvider`s).
42    pub(crate) fn new(scope: InstrumentationScope, provider: SdkTracerProvider) -> Self {
43        SdkTracer {
44            scope: Arc::new(scope),
45            provider,
46        }
47    }
48
49    /// TracerProvider associated with this tracer.
50    pub(crate) fn provider(&self) -> &SdkTracerProvider {
51        &self.provider
52    }
53
54    /// Instrumentation scope of this tracer.
55    pub(crate) fn instrumentation_scope(&self) -> &InstrumentationScope {
56        &self.scope
57    }
58
59    fn build_recording_span(
60        &self,
61        psc: &SpanContext,
62        sc: SpanContext,
63        mut builder: SpanBuilder,
64        attrs: Vec<KeyValue>,
65        span_limits: SpanLimits,
66    ) -> Span {
67        let mut attribute_options = builder.attributes.take().unwrap_or_default();
68        for extra_attr in attrs {
69            attribute_options.push(extra_attr);
70        }
71        let span_attributes_limit = span_limits.max_attributes_per_span as usize;
72        let dropped_attributes_count = attribute_options
73            .len()
74            .saturating_sub(span_attributes_limit);
75        attribute_options.truncate(span_attributes_limit);
76        let dropped_attributes_count = dropped_attributes_count as u32;
77
78        // Links are available as Option<Vec<Link>> in the builder
79        // If it is None, then there are no links to process.
80        // In that case Span.Links will be default (empty Vec<Link>, 0 drop count)
81        // Otherwise, truncate Vec<Link> to keep until limits and use that in Span.Links.
82        // Store the count of excess links into Span.Links.dropped_count.
83        // There is no ability today to add Links after Span creation,
84        // but such a capability will be needed in the future
85        // once the spec for that stabilizes.
86
87        let spans_links_limit = span_limits.max_links_per_span as usize;
88        let span_links: SpanLinks = if let Some(mut links) = builder.links.take() {
89            let dropped_count = links.len().saturating_sub(spans_links_limit);
90            links.truncate(spans_links_limit);
91            let link_attributes_limit = span_limits.max_attributes_per_link as usize;
92            for link in links.iter_mut() {
93                let dropped_attributes_count =
94                    link.attributes.len().saturating_sub(link_attributes_limit);
95                link.attributes.truncate(link_attributes_limit);
96                link.dropped_attributes_count = dropped_attributes_count as u32;
97            }
98            SpanLinks {
99                links,
100                dropped_count: dropped_count as u32,
101            }
102        } else {
103            SpanLinks::default()
104        };
105
106        let SpanBuilder {
107            name,
108            start_time,
109            events,
110            ..
111        } = builder;
112
113        let start_time = start_time.unwrap_or_else(opentelemetry::time::now);
114        let spans_events_limit = span_limits.max_events_per_span as usize;
115        let span_events: SpanEvents = if let Some(mut events) = events {
116            let dropped_count = events.len().saturating_sub(spans_events_limit);
117            events.truncate(spans_events_limit);
118            let event_attributes_limit = span_limits.max_attributes_per_event as usize;
119            for event in events.iter_mut() {
120                let dropped_attributes_count = event
121                    .attributes
122                    .len()
123                    .saturating_sub(event_attributes_limit);
124                event.attributes.truncate(event_attributes_limit);
125                event.dropped_attributes_count = dropped_attributes_count as u32;
126            }
127            SpanEvents {
128                events,
129                dropped_count: dropped_count as u32,
130            }
131        } else {
132            SpanEvents::default()
133        };
134        Span::new(
135            sc,
136            Some(SpanData {
137                parent_span_id: psc.span_id(),
138                parent_span_is_remote: psc.is_valid() && psc.is_remote(),
139                span_kind: builder.span_kind.take().unwrap_or(SpanKind::Internal),
140                name,
141                start_time,
142                end_time: start_time,
143                attributes: attribute_options,
144                dropped_attributes_count,
145                events: span_events,
146                links: span_links,
147                status: Status::default(),
148            }),
149            self.clone(),
150            span_limits,
151        )
152    }
153}
154
155impl opentelemetry::trace::Tracer for SdkTracer {
156    /// This implementation of `Tracer` produces `sdk::Span` instances.
157    type Span = Span;
158
159    /// Starts a span from a `SpanBuilder`.
160    ///
161    /// Each span has zero or one parent spans and zero or more child spans, which
162    /// represent causally related operations. A tree of related spans comprises a
163    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
164    /// trace includes a single root span, which is the shared ancestor of all other
165    /// spans in the trace.
166    fn build_with_context(&self, builder: SpanBuilder, parent_cx: &Context) -> Self::Span {
167        if parent_cx.is_telemetry_suppressed() {
168            return Span::new(
169                SpanContext::empty_context(),
170                None,
171                self.clone(),
172                SpanLimits::default(),
173            );
174        }
175
176        let provider = self.provider();
177        // no point start a span if the tracer provider has already being shutdown
178        if provider.is_shutdown() {
179            return Span::new(
180                SpanContext::empty_context(),
181                None,
182                self.clone(),
183                SpanLimits::default(),
184            );
185        }
186
187        let config = provider.config();
188        let span_id = config.id_generator.new_span_id();
189        let trace_id;
190        let mut psc = &SpanContext::empty_context();
191
192        // An active-but-invalid parent (e.g. a context carrying an invalid active
193        // span context) is not a usable parent: inheriting its all-zero trace id would
194        // produce a span with an invalid trace id if the sampler decides to record.
195        let parent_span = Some(parent_cx.span()).filter(|span| span.span_context().is_valid());
196
197        // Build context for sampling decision
198        if let Some(sc) = parent_span.as_ref().map(|parent| parent.span_context()) {
199            trace_id = sc.trace_id();
200            psc = sc;
201        } else {
202            trace_id = config.id_generator.new_trace_id();
203        };
204
205        let samplings_result = config.sampler.should_sample(
206            Some(parent_cx),
207            trace_id,
208            &builder.name,
209            builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal),
210            builder.attributes.as_ref().unwrap_or(&Vec::new()),
211            builder.links.as_deref().unwrap_or(&[]),
212        );
213
214        let trace_flags = psc.trace_flags();
215        let trace_state = samplings_result.trace_state;
216        let span_limits = config.span_limits;
217        // Build optional inner context, `None` if not recording.
218        let mut span = match samplings_result.decision {
219            SamplingDecision::RecordAndSample => {
220                let sc = SpanContext::new(
221                    trace_id,
222                    span_id,
223                    trace_flags.with_sampled(true),
224                    false,
225                    trace_state,
226                );
227                self.build_recording_span(
228                    psc,
229                    sc,
230                    builder,
231                    samplings_result.attributes,
232                    span_limits,
233                )
234            }
235            SamplingDecision::RecordOnly => {
236                let sc = SpanContext::new(
237                    trace_id,
238                    span_id,
239                    trace_flags.with_sampled(false),
240                    false,
241                    trace_state,
242                );
243                self.build_recording_span(
244                    psc,
245                    sc,
246                    builder,
247                    samplings_result.attributes,
248                    span_limits,
249                )
250            }
251            SamplingDecision::Drop => {
252                let span_context =
253                    SpanContext::new(trace_id, span_id, TraceFlags::default(), false, trace_state);
254                Span::new(span_context, None, self.clone(), span_limits)
255            }
256        };
257
258        if span.is_recording() {
259            // Call `on_start` for all processors
260            for processor in provider.span_processors() {
261                processor.on_start(&mut span, parent_cx)
262            }
263        }
264
265        span
266    }
267}
268
269#[cfg(all(test, feature = "testing", feature = "trace"))]
270mod tests {
271    use crate::{
272        testing::trace::TestSpan,
273        trace::{Sampler, SamplingDecision, SamplingResult, ShouldSample},
274    };
275    use opentelemetry::{
276        trace::{
277            Link, Span, SpanContext, SpanId, SpanKind, TraceContextExt, TraceFlags, TraceId,
278            TraceState, Tracer, TracerProvider,
279        },
280        Context, KeyValue,
281    };
282
283    #[derive(Clone, Debug)]
284    struct TestSampler {}
285
286    impl ShouldSample for TestSampler {
287        fn should_sample(
288            &self,
289            parent_context: Option<&Context>,
290            _trace_id: TraceId,
291            _name: &str,
292            _span_kind: &SpanKind,
293            _attributes: &[KeyValue],
294            _links: &[Link],
295        ) -> SamplingResult {
296            let trace_state = parent_context
297                .unwrap()
298                .span()
299                .span_context()
300                .trace_state()
301                .clone();
302            SamplingResult {
303                decision: SamplingDecision::RecordAndSample,
304                attributes: Vec::new(),
305                trace_state: trace_state.insert("foo", "notbar").unwrap(),
306            }
307        }
308    }
309
310    #[test]
311    fn allow_sampler_to_change_trace_state() {
312        // Setup
313        let sampler = TestSampler {};
314        let tracer_provider = crate::trace::SdkTracerProvider::builder()
315            .with_sampler(sampler)
316            .build();
317        let tracer = tracer_provider.tracer("test");
318        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
319
320        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
321            TraceId::from(128),
322            SpanId::from(64),
323            TraceFlags::SAMPLED,
324            true,
325            trace_state,
326        )));
327
328        // Test sampler should change trace state
329        let span = tracer.start_with_context("foo", &parent_context);
330        let span_context = span.span_context();
331        let expected = span_context.trace_state();
332        assert_eq!(expected.get("foo"), Some("notbar"))
333    }
334
335    #[test]
336    fn allow_sampler_to_change_trace_state_boxed() {
337        // Setup
338        let sampler: Box<dyn ShouldSample> = Box::new(TestSampler {});
339        let tracer_provider = crate::trace::SdkTracerProvider::builder()
340            .with_sampler(sampler)
341            .build();
342        let tracer = tracer_provider.tracer("test");
343        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
344
345        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
346            TraceId::from(128),
347            SpanId::from(64),
348            TraceFlags::SAMPLED,
349            true,
350            trace_state,
351        )));
352
353        // Test sampler should change trace state
354        let span = tracer.start_with_context("foo", &parent_context);
355        let span_context = span.span_context();
356        let expected = span_context.trace_state();
357        assert_eq!(expected.get("foo"), Some("notbar"))
358    }
359
360    #[test]
361    fn drop_parent_based_children() {
362        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
363        let tracer_provider = crate::trace::SdkTracerProvider::builder()
364            .with_sampler(sampler)
365            .build();
366
367        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
368        let tracer = tracer_provider.tracer("test");
369        let span = tracer.start_with_context("must_not_be_sampled", &context);
370
371        assert!(!span.span_context().is_sampled());
372    }
373
374    #[test]
375    fn uses_current_context_for_builders_if_unset() {
376        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
377        let tracer_provider = crate::trace::SdkTracerProvider::builder()
378            .with_sampler(sampler)
379            .build();
380        let tracer = tracer_provider.tracer("test");
381
382        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
383        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
384        assert!(!span.span_context().is_sampled());
385
386        let context = Context::map_current(|cx| {
387            cx.with_remote_span_context(SpanContext::new(
388                TraceId::from(1),
389                SpanId::from(1),
390                TraceFlags::default(),
391                true,
392                Default::default(),
393            ))
394        });
395        let _attached = context.attach();
396        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
397
398        assert!(!span.span_context().is_sampled());
399    }
400
401    #[test]
402    fn in_span_with_context_uses_provided_context() {
403        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
404
405        let exporter = InMemorySpanExporter::default();
406        let tracer_provider = crate::trace::SdkTracerProvider::builder()
407            .with_sampler(Sampler::AlwaysOn)
408            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
409            .build();
410        let tracer = tracer_provider.tracer("test");
411
412        // Create a parent context explicitly
413        let parent_span = tracer.start("parent");
414        let parent_trace_id = parent_span.span_context().trace_id();
415        let parent_span_id = parent_span.span_context().span_id();
416        let parent_cx = Context::current_with_span(parent_span);
417
418        // Use in_span_with_context with explicit parent context
419        let mut child_trace_id = None;
420        let mut child_span_id = None;
421        let mut executed = false;
422
423        let returned_value = tracer.in_span_with_context("child-span", &parent_cx, |cx| {
424            let span = cx.span();
425            child_trace_id = Some(span.span_context().trace_id());
426            child_span_id = Some(span.span_context().span_id());
427            executed = true;
428            "test_result"
429        });
430
431        // Verify child span inherited parent's trace_id
432        assert_eq!(child_trace_id, Some(parent_trace_id));
433        // Verify child has a different span_id than parent
434        assert_ne!(child_span_id, Some(parent_span_id));
435        // Verify the closure was executed
436        assert!(executed);
437        // Verify return value is passed through
438        assert_eq!(returned_value, "test_result");
439
440        // End the parent span to export it
441        drop(parent_cx);
442
443        // Verify parent-child relationship through exporter
444        let spans = exporter.get_finished_spans().unwrap();
445        assert_eq!(spans.len(), 2);
446        let parent = spans.iter().find(|s| s.name == "parent").unwrap();
447        let child = spans.iter().find(|s| s.name == "child-span").unwrap();
448        assert_eq!(child.parent_span_id, parent.span_context.span_id());
449        assert_eq!(
450            child.span_context.trace_id(),
451            parent.span_context.trace_id()
452        );
453    }
454
455    #[test]
456    fn in_span_with_builder_uses_current_context() {
457        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
458
459        let exporter = InMemorySpanExporter::default();
460        let tracer_provider = crate::trace::SdkTracerProvider::builder()
461            .with_sampler(Sampler::AlwaysOn)
462            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
463            .build();
464        let tracer = tracer_provider.tracer("test");
465
466        // Create a parent span and attach it to the current context
467        let parent_span = tracer.start("parent");
468        let parent_trace_id = parent_span.span_context().trace_id();
469        let parent_span_id = parent_span.span_context().span_id();
470        let _attached = Context::current_with_span(parent_span).attach();
471
472        // Use in_span_with_builder with configured span
473        let mut child_trace_id = None;
474
475        tracer.in_span_with_builder(
476            tracer
477                .span_builder("child")
478                .with_kind(SpanKind::Client)
479                .with_attributes(vec![KeyValue::new("test_key", "test_value")]),
480            |cx| {
481                let span = cx.span();
482                child_trace_id = Some(span.span_context().trace_id());
483            },
484        );
485
486        // Verify child span inherited parent's trace_id
487        assert_eq!(child_trace_id, Some(parent_trace_id));
488
489        // End the attached context to export the parent span
490        drop(_attached);
491
492        // Verify parent-child relationship through exporter
493        let spans = exporter.get_finished_spans().unwrap();
494        assert_eq!(spans.len(), 2);
495        let child = spans.iter().find(|s| s.name == "child").unwrap();
496        assert_eq!(child.parent_span_id, parent_span_id);
497        assert_eq!(child.span_context.trace_id(), parent_trace_id);
498    }
499
500    #[test]
501    fn in_span_with_builder_and_context_uses_provided_context() {
502        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
503
504        let exporter = InMemorySpanExporter::default();
505        let tracer_provider = crate::trace::SdkTracerProvider::builder()
506            .with_sampler(Sampler::AlwaysOn)
507            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
508            .build();
509        let tracer = tracer_provider.tracer("test");
510
511        // Create a parent context explicitly
512        let parent_span = tracer.start("parent");
513        let parent_trace_id = parent_span.span_context().trace_id();
514        let parent_span_id = parent_span.span_context().span_id();
515        let parent_cx = Context::current_with_span(parent_span);
516
517        // Use in_span_with_builder_and_context with explicit parent context
518        let mut child_trace_id = None;
519        let mut result = 0;
520
521        let returned_value = tracer.in_span_with_builder_and_context(
522            tracer
523                .span_builder("child")
524                .with_kind(SpanKind::Server)
525                .with_attributes(vec![
526                    KeyValue::new("http.method", "GET"),
527                    KeyValue::new("http.url", "/api/test"),
528                ]),
529            &parent_cx,
530            |cx| {
531                let span = cx.span();
532                child_trace_id = Some(span.span_context().trace_id());
533                result = 42;
534                result
535            },
536        );
537
538        // Verify child span inherited parent's trace_id
539        assert_eq!(child_trace_id, Some(parent_trace_id));
540        // Verify return value is passed through
541        assert_eq!(returned_value, 42);
542        assert_eq!(result, 42);
543
544        // End the parent span to export it
545        drop(parent_cx);
546
547        // Verify parent-child relationship through exporter
548        let spans = exporter.get_finished_spans().unwrap();
549        assert_eq!(spans.len(), 2);
550        let child = spans.iter().find(|s| s.name == "child").unwrap();
551        assert_eq!(child.parent_span_id, parent_span_id);
552        assert_eq!(child.span_context.trace_id(), parent_trace_id);
553    }
554
555    #[test]
556    fn in_span_with_builder_and_context_ignores_active_context() {
557        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
558
559        let exporter = InMemorySpanExporter::default();
560        let tracer_provider = crate::trace::SdkTracerProvider::builder()
561            .with_sampler(Sampler::AlwaysOn)
562            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
563            .build();
564        let tracer = tracer_provider.tracer("test");
565
566        // Create an active context with a specific trace context
567        let active_span_context = SpanContext::new(
568            TraceId::from(1u128),
569            SpanId::from(1u64),
570            TraceFlags::SAMPLED,
571            true,
572            Default::default(),
573        );
574        let active_trace_id = active_span_context.trace_id();
575        let active_span_id = active_span_context.span_id();
576        let _attached = Context::current_with_span(TestSpan(active_span_context)).attach();
577
578        // Create a different parent context with a different trace ID to explicitly provide
579        let provided_span_context = SpanContext::new(
580            TraceId::from(2u128),
581            SpanId::from(2u64),
582            TraceFlags::SAMPLED,
583            true,
584            Default::default(),
585        );
586        let provided_trace_id = provided_span_context.trace_id();
587        let provided_span_id = provided_span_context.span_id();
588        let provided_cx = Context::current_with_span(TestSpan(provided_span_context));
589
590        // Ensure the two parents have different trace IDs
591        assert_ne!(active_trace_id, provided_trace_id);
592
593        // Use in_span_with_builder_and_context with explicit parent context
594        let mut child_trace_id = None;
595
596        tracer.in_span_with_builder_and_context(
597            tracer.span_builder("child").with_kind(SpanKind::Internal),
598            &provided_cx,
599            |cx| {
600                let span = cx.span();
601                child_trace_id = Some(span.span_context().trace_id());
602            },
603        );
604
605        // Verify child uses the provided context, NOT the active context
606        assert_eq!(child_trace_id, Some(provided_trace_id));
607        assert_ne!(child_trace_id, Some(active_trace_id));
608
609        // Verify parent-child relationship through exporter
610        let spans = exporter.get_finished_spans().unwrap();
611        assert_eq!(spans.len(), 1);
612        let child = &spans[0];
613        assert_eq!(child.name, "child");
614        assert_eq!(child.parent_span_id, provided_span_id);
615        assert_eq!(child.span_context.trace_id(), provided_trace_id);
616        assert_ne!(child.parent_span_id, active_span_id);
617        assert_ne!(child.span_context.trace_id(), active_trace_id);
618    }
619
620    #[test]
621    fn invalid_parent_starts_a_new_trace() {
622        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
623
624        let exporter = InMemorySpanExporter::default();
625        let tracer_provider = crate::trace::SdkTracerProvider::builder()
626            // Root is AlwaysOn; the local-not-sampled branch would drop instead.
627            .with_sampler(Sampler::parent_based(Sampler::AlwaysOn))
628            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
629            .build();
630        let tracer = tracer_provider.tracer("test");
631
632        // An active parent whose span context is invalid (e.g. a context carrying
633        // an invalid active span context). It must not be inherited as a parent.
634        let parent_cx = Context::current_with_span(TestSpan(SpanContext::new(
635            TraceId::INVALID,
636            SpanId::from(7),
637            TraceFlags::SAMPLED,
638            true,
639            TraceState::default(),
640        )));
641
642        let span = tracer.build_with_context(tracer.span_builder("child"), &parent_cx);
643        let span_context = span.span_context().clone();
644        drop(span);
645
646        assert!(
647            span_context.trace_id() != TraceId::INVALID,
648            "a recorded span must not inherit an invalid parent's all-zero trace id"
649        );
650        assert!(span_context.is_valid());
651
652        let spans = exporter.get_finished_spans().unwrap();
653        assert_eq!(spans.len(), 1);
654        assert_eq!(spans[0].span_context.trace_id(), span_context.trace_id());
655        assert_eq!(
656            spans[0].parent_span_id,
657            SpanId::INVALID,
658            "an invalid parent must not be recorded as the parent"
659        );
660    }
661}