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    IdGenerator, ShouldSample, SpanEvents, SpanLimits, SpanLinks,
14};
15use opentelemetry::{
16    trace::{
17        SamplingDecision, Span as _, SpanBuilder, SpanContext, SpanKind, TraceContextExt,
18        TraceFlags,
19    },
20    Context, InstrumentationScope, KeyValue,
21};
22use std::fmt;
23
24/// `Tracer` implementation to create and manage spans
25#[derive(Clone)]
26pub struct SdkTracer {
27    scope: InstrumentationScope,
28    provider: SdkTracerProvider,
29}
30
31impl fmt::Debug for SdkTracer {
32    /// Formats the `Tracer` using the given formatter.
33    /// Omitting `provider` here is necessary to avoid cycles.
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        f.debug_struct("Tracer")
36            .field("name", &self.scope.name())
37            .field("version", &self.scope.version())
38            .finish()
39    }
40}
41
42impl SdkTracer {
43    /// Create a new tracer (used internally by `TracerProvider`s).
44    pub(crate) fn new(scope: InstrumentationScope, provider: SdkTracerProvider) -> Self {
45        SdkTracer { scope, provider }
46    }
47
48    /// TracerProvider associated with this tracer.
49    pub(crate) fn provider(&self) -> &SdkTracerProvider {
50        &self.provider
51    }
52
53    /// Instrumentation scope of this tracer.
54    pub(crate) fn instrumentation_scope(&self) -> &InstrumentationScope {
55        &self.scope
56    }
57
58    fn build_recording_span(
59        &self,
60        psc: &SpanContext,
61        sc: SpanContext,
62        mut builder: SpanBuilder,
63        attrs: Vec<KeyValue>,
64        span_limits: SpanLimits,
65    ) -> Span {
66        let mut attribute_options = builder.attributes.take().unwrap_or_default();
67        for extra_attr in attrs {
68            attribute_options.push(extra_attr);
69        }
70        let span_attributes_limit = span_limits.max_attributes_per_span as usize;
71        let dropped_attributes_count = attribute_options
72            .len()
73            .saturating_sub(span_attributes_limit);
74        attribute_options.truncate(span_attributes_limit);
75        let dropped_attributes_count = dropped_attributes_count as u32;
76
77        // Links are available as Option<Vec<Link>> in the builder
78        // If it is None, then there are no links to process.
79        // In that case Span.Links will be default (empty Vec<Link>, 0 drop count)
80        // Otherwise, truncate Vec<Link> to keep until limits and use that in Span.Links.
81        // Store the count of excess links into Span.Links.dropped_count.
82        // There is no ability today to add Links after Span creation,
83        // but such a capability will be needed in the future
84        // once the spec for that stabilizes.
85
86        let spans_links_limit = span_limits.max_links_per_span as usize;
87        let span_links: SpanLinks = if let Some(mut links) = builder.links.take() {
88            let dropped_count = links.len().saturating_sub(spans_links_limit);
89            links.truncate(spans_links_limit);
90            let link_attributes_limit = span_limits.max_attributes_per_link as usize;
91            for link in links.iter_mut() {
92                let dropped_attributes_count =
93                    link.attributes.len().saturating_sub(link_attributes_limit);
94                link.attributes.truncate(link_attributes_limit);
95                link.dropped_attributes_count = dropped_attributes_count as u32;
96            }
97            SpanLinks {
98                links,
99                dropped_count: dropped_count as u32,
100            }
101        } else {
102            SpanLinks::default()
103        };
104
105        let SpanBuilder {
106            name,
107            start_time,
108            end_time,
109            events,
110            status,
111            ..
112        } = builder;
113
114        let start_time = start_time.unwrap_or_else(opentelemetry::time::now);
115        let end_time = end_time.unwrap_or(start_time);
116        let spans_events_limit = span_limits.max_events_per_span as usize;
117        let span_events: SpanEvents = if let Some(mut events) = events {
118            let dropped_count = events.len().saturating_sub(spans_events_limit);
119            events.truncate(spans_events_limit);
120            let event_attributes_limit = span_limits.max_attributes_per_event as usize;
121            for event in events.iter_mut() {
122                let dropped_attributes_count = event
123                    .attributes
124                    .len()
125                    .saturating_sub(event_attributes_limit);
126                event.attributes.truncate(event_attributes_limit);
127                event.dropped_attributes_count = dropped_attributes_count as u32;
128            }
129            SpanEvents {
130                events,
131                dropped_count: dropped_count as u32,
132            }
133        } else {
134            SpanEvents::default()
135        };
136        Span::new(
137            sc,
138            Some(SpanData {
139                parent_span_id: psc.span_id(),
140                parent_span_is_remote: psc.is_valid() && psc.is_remote(),
141                span_kind: builder.span_kind.take().unwrap_or(SpanKind::Internal),
142                name,
143                start_time,
144                end_time,
145                attributes: attribute_options,
146                dropped_attributes_count,
147                events: span_events,
148                links: span_links,
149                status,
150            }),
151            self.clone(),
152            span_limits,
153        )
154    }
155
156    /// The [`IdGenerator`] associated with this tracer.
157    ///
158    // Note: this is necessary for tracing-opentelemetry's `PreSampledTracer`.
159    #[doc(hidden)]
160    pub fn id_generator(&self) -> &dyn IdGenerator {
161        &*self.provider.config().id_generator
162    }
163
164    /// The [`ShouldSample`] associated with this tracer.
165    ///
166    // Note: this is necessary for tracing-opentelemetry's `PreSampledTracer`.
167    #[doc(hidden)]
168    pub fn should_sample(&self) -> &dyn ShouldSample {
169        &*self.provider.config().sampler
170    }
171}
172
173impl opentelemetry::trace::Tracer for SdkTracer {
174    /// This implementation of `Tracer` produces `sdk::Span` instances.
175    type Span = Span;
176
177    /// Starts a span from a `SpanBuilder`.
178    ///
179    /// Each span has zero or one parent spans and zero or more child spans, which
180    /// represent causally related operations. A tree of related spans comprises a
181    /// trace. A span is said to be a _root span_ if it does not have a parent. Each
182    /// trace includes a single root span, which is the shared ancestor of all other
183    /// spans in the trace.
184    fn build_with_context(&self, mut builder: SpanBuilder, parent_cx: &Context) -> Self::Span {
185        if parent_cx.is_telemetry_suppressed() {
186            return Span::new(
187                SpanContext::empty_context(),
188                None,
189                self.clone(),
190                SpanLimits::default(),
191            );
192        }
193
194        let provider = self.provider();
195        // no point start a span if the tracer provider has already being shutdown
196        if provider.is_shutdown() {
197            return Span::new(
198                SpanContext::empty_context(),
199                None,
200                self.clone(),
201                SpanLimits::default(),
202            );
203        }
204
205        let config = provider.config();
206        let span_id = builder
207            .span_id
208            .take()
209            .unwrap_or_else(|| config.id_generator.new_span_id());
210        let trace_id;
211        let mut psc = &SpanContext::empty_context();
212
213        let parent_span = if parent_cx.has_active_span() {
214            Some(parent_cx.span())
215        } else {
216            None
217        };
218
219        // Build context for sampling decision
220        if let Some(sc) = parent_span.as_ref().map(|parent| parent.span_context()) {
221            trace_id = sc.trace_id();
222            psc = sc;
223        } else {
224            trace_id = builder
225                .trace_id
226                .unwrap_or_else(|| config.id_generator.new_trace_id());
227        };
228
229        // In order to accommodate use cases like `tracing-opentelemetry` we there is the ability
230        // to use pre-sampling. Otherwise, the standard method of sampling is followed.
231        let samplings_result = if let Some(sr) = builder.sampling_result.take() {
232            sr
233        } else {
234            config.sampler.should_sample(
235                Some(parent_cx),
236                trace_id,
237                &builder.name,
238                builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal),
239                builder.attributes.as_ref().unwrap_or(&Vec::new()),
240                builder.links.as_deref().unwrap_or(&[]),
241            )
242        };
243
244        let trace_flags = parent_cx.span().span_context().trace_flags();
245        let trace_state = samplings_result.trace_state;
246        let span_limits = config.span_limits;
247        // Build optional inner context, `None` if not recording.
248        let mut span = match samplings_result.decision {
249            SamplingDecision::RecordAndSample => {
250                let sc = SpanContext::new(
251                    trace_id,
252                    span_id,
253                    trace_flags.with_sampled(true),
254                    false,
255                    trace_state,
256                );
257                self.build_recording_span(
258                    psc,
259                    sc,
260                    builder,
261                    samplings_result.attributes,
262                    span_limits,
263                )
264            }
265            SamplingDecision::RecordOnly => {
266                let sc = SpanContext::new(
267                    trace_id,
268                    span_id,
269                    trace_flags.with_sampled(false),
270                    false,
271                    trace_state,
272                );
273                self.build_recording_span(
274                    psc,
275                    sc,
276                    builder,
277                    samplings_result.attributes,
278                    span_limits,
279                )
280            }
281            SamplingDecision::Drop => {
282                let span_context =
283                    SpanContext::new(trace_id, span_id, TraceFlags::default(), false, trace_state);
284                Span::new(span_context, None, self.clone(), span_limits)
285            }
286        };
287
288        if span.is_recording() {
289            // Call `on_start` for all processors
290            for processor in provider.span_processors() {
291                processor.on_start(&mut span, parent_cx)
292            }
293        }
294
295        span
296    }
297}
298
299#[cfg(all(test, feature = "testing", feature = "trace"))]
300mod tests {
301    use crate::{
302        testing::trace::TestSpan,
303        trace::{Sampler, ShouldSample},
304    };
305    use opentelemetry::{
306        trace::{
307            Link, SamplingDecision, SamplingResult, Span, SpanContext, SpanId, SpanKind,
308            TraceContextExt, TraceFlags, TraceId, TraceState, Tracer, TracerProvider,
309        },
310        Context, KeyValue,
311    };
312
313    #[derive(Clone, Debug)]
314    struct TestSampler {}
315
316    impl ShouldSample for TestSampler {
317        fn should_sample(
318            &self,
319            parent_context: Option<&Context>,
320            _trace_id: TraceId,
321            _name: &str,
322            _span_kind: &SpanKind,
323            _attributes: &[KeyValue],
324            _links: &[Link],
325        ) -> SamplingResult {
326            let trace_state = parent_context
327                .unwrap()
328                .span()
329                .span_context()
330                .trace_state()
331                .clone();
332            SamplingResult {
333                decision: SamplingDecision::RecordAndSample,
334                attributes: Vec::new(),
335                trace_state: trace_state.insert("foo", "notbar").unwrap(),
336            }
337        }
338    }
339
340    #[test]
341    fn allow_sampler_to_change_trace_state() {
342        // Setup
343        let sampler = TestSampler {};
344        let tracer_provider = crate::trace::SdkTracerProvider::builder()
345            .with_sampler(sampler)
346            .build();
347        let tracer = tracer_provider.tracer("test");
348        let trace_state = TraceState::from_key_value(vec![("foo", "bar")]).unwrap();
349
350        let parent_context = Context::new().with_span(TestSpan(SpanContext::new(
351            TraceId::from(128),
352            SpanId::from(64),
353            TraceFlags::SAMPLED,
354            true,
355            trace_state,
356        )));
357
358        // Test sampler should change trace state
359        let span = tracer.start_with_context("foo", &parent_context);
360        let span_context = span.span_context();
361        let expected = span_context.trace_state();
362        assert_eq!(expected.get("foo"), Some("notbar"))
363    }
364
365    #[test]
366    fn drop_parent_based_children() {
367        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
368        let tracer_provider = crate::trace::SdkTracerProvider::builder()
369            .with_sampler(sampler)
370            .build();
371
372        let context = Context::current_with_span(TestSpan(SpanContext::empty_context()));
373        let tracer = tracer_provider.tracer("test");
374        let span = tracer.start_with_context("must_not_be_sampled", &context);
375
376        assert!(!span.span_context().is_sampled());
377    }
378
379    #[test]
380    fn uses_current_context_for_builders_if_unset() {
381        let sampler = Sampler::ParentBased(Box::new(Sampler::AlwaysOn));
382        let tracer_provider = crate::trace::SdkTracerProvider::builder()
383            .with_sampler(sampler)
384            .build();
385        let tracer = tracer_provider.tracer("test");
386
387        let _attached = Context::current_with_span(TestSpan(SpanContext::empty_context())).attach();
388        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
389        assert!(!span.span_context().is_sampled());
390
391        let context = Context::map_current(|cx| {
392            cx.with_remote_span_context(SpanContext::new(
393                TraceId::from(1),
394                SpanId::from(1),
395                TraceFlags::default(),
396                true,
397                Default::default(),
398            ))
399        });
400        let _attached = context.attach();
401        let span = tracer.span_builder("must_not_be_sampled").start(&tracer);
402
403        assert!(!span.span_context().is_sampled());
404    }
405}