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        let parent_span = if parent_cx.has_active_span() {
193            Some(parent_cx.span())
194        } else {
195            None
196        };
197
198        // Build context for sampling decision
199        if let Some(sc) = parent_span.as_ref().map(|parent| parent.span_context()) {
200            trace_id = sc.trace_id();
201            psc = sc;
202        } else {
203            trace_id = config.id_generator.new_trace_id();
204        };
205
206        let samplings_result = config.sampler.should_sample(
207            Some(parent_cx),
208            trace_id,
209            &builder.name,
210            builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal),
211            builder.attributes.as_ref().unwrap_or(&Vec::new()),
212            builder.links.as_deref().unwrap_or(&[]),
213        );
214
215        let trace_flags = parent_cx.span().span_context().trace_flags();
216        let trace_state = samplings_result.trace_state;
217        let span_limits = config.span_limits;
218        // Build optional inner context, `None` if not recording.
219        let mut span = match samplings_result.decision {
220            SamplingDecision::RecordAndSample => {
221                let sc = SpanContext::new(
222                    trace_id,
223                    span_id,
224                    trace_flags.with_sampled(true),
225                    false,
226                    trace_state,
227                );
228                self.build_recording_span(
229                    psc,
230                    sc,
231                    builder,
232                    samplings_result.attributes,
233                    span_limits,
234                )
235            }
236            SamplingDecision::RecordOnly => {
237                let sc = SpanContext::new(
238                    trace_id,
239                    span_id,
240                    trace_flags.with_sampled(false),
241                    false,
242                    trace_state,
243                );
244                self.build_recording_span(
245                    psc,
246                    sc,
247                    builder,
248                    samplings_result.attributes,
249                    span_limits,
250                )
251            }
252            SamplingDecision::Drop => {
253                let span_context =
254                    SpanContext::new(trace_id, span_id, TraceFlags::default(), false, trace_state);
255                Span::new(span_context, None, self.clone(), span_limits)
256            }
257        };
258
259        if span.is_recording() {
260            // Call `on_start` for all processors
261            for processor in provider.span_processors() {
262                processor.on_start(&mut span, parent_cx)
263            }
264        }
265
266        span
267    }
268}
269
270#[cfg(all(test, feature = "testing", feature = "trace"))]
271mod tests {
272    use crate::{
273        testing::trace::TestSpan,
274        trace::{Sampler, SamplingDecision, SamplingResult, ShouldSample},
275    };
276    use opentelemetry::{
277        trace::{
278            Link, Span, SpanContext, SpanId, SpanKind, TraceContextExt, TraceFlags, TraceId,
279            TraceState, Tracer, TracerProvider,
280        },
281        Context, KeyValue,
282    };
283
284    #[derive(Clone, Debug)]
285    struct TestSampler {}
286
287    impl ShouldSample for TestSampler {
288        fn should_sample(
289            &self,
290            parent_context: Option<&Context>,
291            _trace_id: TraceId,
292            _name: &str,
293            _span_kind: &SpanKind,
294            _attributes: &[KeyValue],
295            _links: &[Link],
296        ) -> SamplingResult {
297            let trace_state = parent_context
298                .unwrap()
299                .span()
300                .span_context()
301                .trace_state()
302                .clone();
303            SamplingResult {
304                decision: SamplingDecision::RecordAndSample,
305                attributes: Vec::new(),
306                trace_state: trace_state.insert("foo", "notbar").unwrap(),
307            }
308        }
309    }
310
311    #[test]
312    fn allow_sampler_to_change_trace_state() {
313        // Setup
314        let sampler = TestSampler {};
315        let tracer_provider = crate::trace::SdkTracerProvider::builder()
316            .with_sampler(sampler)
317            .build();
318        let tracer = tracer_provider.tracer("test");
319        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
320
321        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
322            TraceId::from(128),
323            SpanId::from(64),
324            TraceFlags::SAMPLED,
325            true,
326            trace_state,
327        )));
328
329        // Test sampler should change trace state
330        let span = tracer.start_with_context("foo", &parent_context);
331        let span_context = span.span_context();
332        let expected = span_context.trace_state();
333        assert_eq!(expected.get("foo"), Some("notbar"))
334    }
335
336    #[test]
337    fn allow_sampler_to_change_trace_state_boxed() {
338        // Setup
339        let sampler: Box<dyn ShouldSample> = Box::new(TestSampler {});
340        let tracer_provider = crate::trace::SdkTracerProvider::builder()
341            .with_sampler(sampler)
342            .build();
343        let tracer = tracer_provider.tracer("test");
344        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
345
346        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
347            TraceId::from(128),
348            SpanId::from(64),
349            TraceFlags::SAMPLED,
350            true,
351            trace_state,
352        )));
353
354        // Test sampler should change trace state
355        let span = tracer.start_with_context("foo", &parent_context);
356        let span_context = span.span_context();
357        let expected = span_context.trace_state();
358        assert_eq!(expected.get("foo"), Some("notbar"))
359    }
360
361    #[test]
362    fn drop_parent_based_children() {
363        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
364        let tracer_provider = crate::trace::SdkTracerProvider::builder()
365            .with_sampler(sampler)
366            .build();
367
368        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
369        let tracer = tracer_provider.tracer("test");
370        let span = tracer.start_with_context("must_not_be_sampled", &context);
371
372        assert!(!span.span_context().is_sampled());
373    }
374
375    #[test]
376    fn uses_current_context_for_builders_if_unset() {
377        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
378        let tracer_provider = crate::trace::SdkTracerProvider::builder()
379            .with_sampler(sampler)
380            .build();
381        let tracer = tracer_provider.tracer("test");
382
383        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
384        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
385        assert!(!span.span_context().is_sampled());
386
387        let context = Context::map_current(|cx| {
388            cx.with_remote_span_context(SpanContext::new(
389                TraceId::from(1),
390                SpanId::from(1),
391                TraceFlags::default(),
392                true,
393                Default::default(),
394            ))
395        });
396        let _attached = context.attach();
397        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
398
399        assert!(!span.span_context().is_sampled());
400    }
401
402    #[test]
403    fn in_span_with_context_uses_provided_context() {
404        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
405
406        let exporter = InMemorySpanExporter::default();
407        let tracer_provider = crate::trace::SdkTracerProvider::builder()
408            .with_sampler(Sampler::AlwaysOn)
409            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
410            .build();
411        let tracer = tracer_provider.tracer("test");
412
413        // Create a parent context explicitly
414        let parent_span = tracer.start("parent");
415        let parent_trace_id = parent_span.span_context().trace_id();
416        let parent_span_id = parent_span.span_context().span_id();
417        let parent_cx = Context::current_with_span(parent_span);
418
419        // Use in_span_with_context with explicit parent context
420        let mut child_trace_id = None;
421        let mut child_span_id = None;
422        let mut executed = false;
423
424        let returned_value = tracer.in_span_with_context("child-span", &parent_cx, |cx| {
425            let span = cx.span();
426            child_trace_id = Some(span.span_context().trace_id());
427            child_span_id = Some(span.span_context().span_id());
428            executed = true;
429            "test_result"
430        });
431
432        // Verify child span inherited parent's trace_id
433        assert_eq!(child_trace_id, Some(parent_trace_id));
434        // Verify child has a different span_id than parent
435        assert_ne!(child_span_id, Some(parent_span_id));
436        // Verify the closure was executed
437        assert!(executed);
438        // Verify return value is passed through
439        assert_eq!(returned_value, "test_result");
440
441        // End the parent span to export it
442        drop(parent_cx);
443
444        // Verify parent-child relationship through exporter
445        let spans = exporter.get_finished_spans().unwrap();
446        assert_eq!(spans.len(), 2);
447        let parent = spans.iter().find(|s| s.name == "parent").unwrap();
448        let child = spans.iter().find(|s| s.name == "child-span").unwrap();
449        assert_eq!(child.parent_span_id, parent.span_context.span_id());
450        assert_eq!(
451            child.span_context.trace_id(),
452            parent.span_context.trace_id()
453        );
454    }
455
456    #[test]
457    fn in_span_with_builder_uses_current_context() {
458        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
459
460        let exporter = InMemorySpanExporter::default();
461        let tracer_provider = crate::trace::SdkTracerProvider::builder()
462            .with_sampler(Sampler::AlwaysOn)
463            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
464            .build();
465        let tracer = tracer_provider.tracer("test");
466
467        // Create a parent span and attach it to the current context
468        let parent_span = tracer.start("parent");
469        let parent_trace_id = parent_span.span_context().trace_id();
470        let parent_span_id = parent_span.span_context().span_id();
471        let _attached = Context::current_with_span(parent_span).attach();
472
473        // Use in_span_with_builder with configured span
474        let mut child_trace_id = None;
475
476        tracer.in_span_with_builder(
477            tracer
478                .span_builder("child")
479                .with_kind(SpanKind::Client)
480                .with_attributes(vec![KeyValue::new("test_key", "test_value")]),
481            |cx| {
482                let span = cx.span();
483                child_trace_id = Some(span.span_context().trace_id());
484            },
485        );
486
487        // Verify child span inherited parent's trace_id
488        assert_eq!(child_trace_id, Some(parent_trace_id));
489
490        // End the attached context to export the parent span
491        drop(_attached);
492
493        // Verify parent-child relationship through exporter
494        let spans = exporter.get_finished_spans().unwrap();
495        assert_eq!(spans.len(), 2);
496        let child = spans.iter().find(|s| s.name == "child").unwrap();
497        assert_eq!(child.parent_span_id, parent_span_id);
498        assert_eq!(child.span_context.trace_id(), parent_trace_id);
499    }
500
501    #[test]
502    fn in_span_with_builder_and_context_uses_provided_context() {
503        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
504
505        let exporter = InMemorySpanExporter::default();
506        let tracer_provider = crate::trace::SdkTracerProvider::builder()
507            .with_sampler(Sampler::AlwaysOn)
508            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
509            .build();
510        let tracer = tracer_provider.tracer("test");
511
512        // Create a parent context explicitly
513        let parent_span = tracer.start("parent");
514        let parent_trace_id = parent_span.span_context().trace_id();
515        let parent_span_id = parent_span.span_context().span_id();
516        let parent_cx = Context::current_with_span(parent_span);
517
518        // Use in_span_with_builder_and_context with explicit parent context
519        let mut child_trace_id = None;
520        let mut result = 0;
521
522        let returned_value = tracer.in_span_with_builder_and_context(
523            tracer
524                .span_builder("child")
525                .with_kind(SpanKind::Server)
526                .with_attributes(vec![
527                    KeyValue::new("http.method", "GET"),
528                    KeyValue::new("http.url", "/api/test"),
529                ]),
530            &parent_cx,
531            |cx| {
532                let span = cx.span();
533                child_trace_id = Some(span.span_context().trace_id());
534                result = 42;
535                result
536            },
537        );
538
539        // Verify child span inherited parent's trace_id
540        assert_eq!(child_trace_id, Some(parent_trace_id));
541        // Verify return value is passed through
542        assert_eq!(returned_value, 42);
543        assert_eq!(result, 42);
544
545        // End the parent span to export it
546        drop(parent_cx);
547
548        // Verify parent-child relationship through exporter
549        let spans = exporter.get_finished_spans().unwrap();
550        assert_eq!(spans.len(), 2);
551        let child = spans.iter().find(|s| s.name == "child").unwrap();
552        assert_eq!(child.parent_span_id, parent_span_id);
553        assert_eq!(child.span_context.trace_id(), parent_trace_id);
554    }
555
556    #[test]
557    fn in_span_with_builder_and_context_ignores_active_context() {
558        use crate::trace::{InMemorySpanExporter, SimpleSpanProcessor};
559
560        let exporter = InMemorySpanExporter::default();
561        let tracer_provider = crate::trace::SdkTracerProvider::builder()
562            .with_sampler(Sampler::AlwaysOn)
563            .with_span_processor(SimpleSpanProcessor::new(exporter.clone()))
564            .build();
565        let tracer = tracer_provider.tracer("test");
566
567        // Create an active context with a specific trace context
568        let active_span_context = SpanContext::new(
569            TraceId::from(1u128),
570            SpanId::from(1u64),
571            TraceFlags::SAMPLED,
572            true,
573            Default::default(),
574        );
575        let active_trace_id = active_span_context.trace_id();
576        let active_span_id = active_span_context.span_id();
577        let _attached = Context::current_with_span(TestSpan(active_span_context)).attach();
578
579        // Create a different parent context with a different trace ID to explicitly provide
580        let provided_span_context = SpanContext::new(
581            TraceId::from(2u128),
582            SpanId::from(2u64),
583            TraceFlags::SAMPLED,
584            true,
585            Default::default(),
586        );
587        let provided_trace_id = provided_span_context.trace_id();
588        let provided_span_id = provided_span_context.span_id();
589        let provided_cx = Context::current_with_span(TestSpan(provided_span_context));
590
591        // Ensure the two parents have different trace IDs
592        assert_ne!(active_trace_id, provided_trace_id);
593
594        // Use in_span_with_builder_and_context with explicit parent context
595        let mut child_trace_id = None;
596
597        tracer.in_span_with_builder_and_context(
598            tracer.span_builder("child").with_kind(SpanKind::Internal),
599            &provided_cx,
600            |cx| {
601                let span = cx.span();
602                child_trace_id = Some(span.span_context().trace_id());
603            },
604        );
605
606        // Verify child uses the provided context, NOT the active context
607        assert_eq!(child_trace_id, Some(provided_trace_id));
608        assert_ne!(child_trace_id, Some(active_trace_id));
609
610        // Verify parent-child relationship through exporter
611        let spans = exporter.get_finished_spans().unwrap();
612        assert_eq!(spans.len(), 1);
613        let child = &spans[0];
614        assert_eq!(child.name, "child");
615        assert_eq!(child.parent_span_id, provided_span_id);
616        assert_eq!(child.span_context.trace_id(), provided_trace_id);
617        assert_ne!(child.parent_span_id, active_span_id);
618        assert_ne!(child.span_context.trace_id(), active_trace_id);
619    }
620}