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