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