Skip to main content

opentelemetry/trace/
tracer.rs

1use crate::{
2    trace::{Event, Link, Span, SpanKind, TraceContextExt},
3    Context, KeyValue,
4};
5use std::borrow::Cow;
6use std::time::SystemTime;
7
8/// The interface for constructing [`Span`]s.
9///
10/// ## In Synchronous Code
11///
12/// Spans can be created and nested manually:
13///
14/// ```
15/// use opentelemetry::{global, trace::{Span, Tracer, TraceContextExt}, Context};
16///
17/// let tracer = global::tracer("my-component");
18///
19/// let parent = tracer.start("foo");
20/// let parent_cx = Context::current_with_span(parent);
21/// let mut child = tracer.start_with_context("bar", &parent_cx);
22///
23/// // ...
24///
25/// child.end(); // explicitly end
26/// drop(parent_cx) // or implicitly end on drop
27/// ```
28///
29/// Spans can also use the current thread's [`Context`] to track which span is active:
30///
31/// ```
32/// use opentelemetry::{global, trace::{SpanKind, Tracer}};
33///
34/// let tracer = global::tracer("my-component");
35///
36/// // Create simple spans with `in_span`
37/// tracer.in_span("foo", |_foo_cx| {
38///     // parent span is active
39///     tracer.in_span("bar", |_bar_cx| {
40///         // child span is now the active span and associated with the parent span
41///     });
42///     // child has ended, parent now the active span again
43/// });
44/// // parent has ended, no active spans
45/// ```
46///
47/// Spans can also be marked as active, and the resulting guard allows for
48/// greater control over when the span is no longer considered active.
49///
50/// ```
51/// use opentelemetry::{global, trace::{Span, Tracer, mark_span_as_active}};
52/// let tracer = global::tracer("my-component");
53///
54/// let parent_span = tracer.start("foo");
55/// let parent_active = mark_span_as_active(parent_span);
56///
57/// {
58///     let child = tracer.start("bar");
59///     let _child_active = mark_span_as_active(child);
60///
61///     // do work in the context of the child span...
62///
63///     // exiting the scope drops the guard, child is no longer active
64/// }
65/// // Parent is active span again
66///
67/// // Parent can be dropped manually, or allowed to go out of scope as well.
68/// drop(parent_active);
69///
70/// // no active span
71/// ```
72///
73/// ## In Asynchronous Code
74///
75/// If you are instrumenting code that make use of [`std::future::Future`] or
76/// async/await, be sure to use the [`FutureExt`] trait. This is needed because
77/// the following example _will not_ work:
78///
79/// ```no_run
80/// # use opentelemetry::{global, trace::{Tracer, mark_span_as_active}};
81/// # let tracer = global::tracer("foo");
82/// # let span = tracer.start("foo-span");
83/// async {
84///     // Does not work
85///     let _g = mark_span_as_active(span);
86///     // ...
87/// };
88/// ```
89///
90/// The context guard `_g` will not exit until the future generated by the
91/// `async` block is complete. Since futures can be entered and exited
92/// _multiple_ times without them completing, the span remains active for as
93/// long as the future exists, rather than only when it is polled, leading to
94/// very confusing and incorrect output.
95///
96/// In order to trace asynchronous code, the [`Future::with_context`] combinator
97/// can be used:
98///
99/// ```
100/// # async fn run() -> Result<(), ()> {
101/// use opentelemetry::{trace::FutureExt, Context};
102/// let cx = Context::current();
103///
104/// let my_future = async {
105///     // ...
106/// };
107///
108/// my_future
109///     .with_context(cx)
110///     .await;
111/// # Ok(())
112/// # }
113/// ```
114///
115/// [`Future::with_context`] attaches a context to the future, ensuring that the
116/// context's lifetime is as long as the future's.
117///
118/// [`FutureExt`]: crate::trace::FutureExt
119/// [`Future::with_context`]: crate::trace::FutureExt::with_context()
120/// [`Context`]: crate::Context
121pub trait Tracer {
122    /// The [`Span`] type used by this tracer.
123    type Span: Span;
124
125    /// Starts a new [`Span`].
126    ///
127    /// By default the currently active `Span` is set as the new `Span`'s parent.
128    ///
129    /// Each span has zero or one parent span and zero or more child spans, which
130    /// represent causally related operations. A tree of related spans comprises a
131    /// trace. A span is said to be a root span if it does not have a parent. Each
132    /// trace includes a single root span, which is the shared ancestor of all other
133    /// spans in the trace.
134    fn start<T>(&self, name: T) -> Self::Span
135    where
136        T: Into<Cow<'static, str>>,
137    {
138        Context::map_current(|cx| self.start_with_context(name, cx))
139    }
140
141    /// Starts a new [`Span`] with a given context.
142    ///
143    /// If this context contains a span, the newly created span will be a child of
144    /// that span.
145    ///
146    /// Each span has zero or one parent span and zero or more child spans, which
147    /// represent causally related operations. A tree of related spans comprises a
148    /// trace. A span is said to be a root span if it does not have a parent. Each
149    /// trace includes a single root span, which is the shared ancestor of all other
150    /// spans in the trace.
151    fn start_with_context<T>(&self, name: T, parent_cx: &Context) -> Self::Span
152    where
153        T: Into<Cow<'static, str>>,
154    {
155        self.build_with_context(SpanBuilder::from_name(name), parent_cx)
156    }
157
158    /// Creates a span builder.
159    ///
160    /// [`SpanBuilder`]s allow you to specify all attributes of a [`Span`] before
161    /// the span is started.
162    fn span_builder<T>(&self, name: T) -> SpanBuilder
163    where
164        T: Into<Cow<'static, str>>,
165    {
166        SpanBuilder::from_name(name)
167    }
168
169    /// Start a [`Span`] from a [`SpanBuilder`].
170    fn build(&self, builder: SpanBuilder) -> Self::Span {
171        Context::map_current(|cx| self.build_with_context(builder, cx))
172    }
173
174    /// Start a span from a [`SpanBuilder`] with a parent context.
175    fn build_with_context(&self, builder: SpanBuilder, parent_cx: &Context) -> Self::Span;
176
177    /// Start a new span and execute the given closure with reference to the context
178    /// in which the span is active.
179    ///
180    /// This method starts a new span and sets it as the active span for the given
181    /// function. It then executes the body. It ends the span before returning the
182    /// execution result.
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use opentelemetry::{global, trace::{Span, Tracer, get_active_span}, KeyValue};
188    ///
189    /// fn my_function() {
190    ///     // start an active span in one function
191    ///     global::tracer("my-component").in_span("span-name", |_cx| {
192    ///         // anything happening in functions we call can still access the active span...
193    ///         my_other_function();
194    ///     })
195    /// }
196    ///
197    /// fn my_other_function() {
198    ///     // call methods on the current span from
199    ///     get_active_span(|span| {
200    ///         span.add_event("An event!".to_string(), vec![KeyValue::new("happened", true)]);
201    ///     })
202    /// }
203    /// ```
204    fn in_span<T, F, N>(&self, name: N, f: F) -> T
205    where
206        F: FnOnce(Context) -> T,
207        N: Into<Cow<'static, str>>,
208        Self::Span: Send + Sync + 'static,
209    {
210        self.in_span_with_builder(self.span_builder(name), f)
211    }
212
213    /// Start a new span with a given parent context and execute the given closure with
214    /// reference to the context in which the span is active.
215    ///
216    /// This method starts a new span as a child of the provided parent context and sets
217    /// it as the active span for the given function. It then executes the body. It ends
218    /// the span before returning the execution result.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use opentelemetry::{global, trace::{Span, Tracer, TraceContextExt}, Context, KeyValue};
224    ///
225    /// fn my_function() {
226    ///     let tracer = global::tracer("my-component");
227    ///
228    ///     // Create a parent context
229    ///     let parent = tracer.start("parent-span");
230    ///     let parent_cx = Context::current_with_span(parent);
231    ///
232    ///     // start an active span with explicit parent context
233    ///     tracer.in_span_with_context("child-span", &parent_cx, |_cx| {
234    ///         // child span is active here
235    ///     })
236    /// }
237    /// ```
238    fn in_span_with_context<T, F, N>(&self, name: N, parent_cx: &Context, f: F) -> T
239    where
240        F: FnOnce(Context) -> T,
241        N: Into<Cow<'static, str>>,
242        Self::Span: Send + Sync + 'static,
243    {
244        self.in_span_with_builder_and_context(self.span_builder(name), parent_cx, f)
245    }
246
247    /// Start a new span from a [`SpanBuilder`] and execute the given closure with
248    /// reference to the context in which the span is active.
249    ///
250    /// This method builds and starts a new span from a [`SpanBuilder`] using the current
251    /// context and sets it as the active span for the given function. It then executes
252    /// the body. It ends the span before returning the execution result.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// use opentelemetry::{global, trace::{Span, SpanKind, Tracer}, KeyValue};
258    ///
259    /// fn my_function() {
260    ///     let tracer = global::tracer("my-component");
261    ///
262    ///     // start an active span with span configuration
263    ///     tracer.in_span_with_builder(
264    ///         tracer.span_builder("span-name")
265    ///             .with_kind(SpanKind::Client)
266    ///             .with_attributes(vec![KeyValue::new("key", "value")]),
267    ///         |_cx| {
268    ///             // span is active here with configured attributes
269    ///         }
270    ///     )
271    /// }
272    /// ```
273    fn in_span_with_builder<T, F>(&self, builder: SpanBuilder, f: F) -> T
274    where
275        F: FnOnce(Context) -> T,
276        Self::Span: Send + Sync + 'static,
277    {
278        let span = self.build(builder);
279        let cx = Context::current_with_span(span);
280        let _guard = cx.clone().attach();
281        f(cx)
282    }
283
284    /// Start a new span from a [`SpanBuilder`] with a given parent context and execute the
285    /// given closure with reference to the context in which the span is active.
286    ///
287    /// This method builds and starts a new span from a [`SpanBuilder`] as a child of the
288    /// provided parent context and sets it as the active span for the given function. It
289    /// then executes the body. It ends the span before returning the execution result.
290    ///
291    /// # Examples
292    ///
293    /// ```
294    /// use opentelemetry::{global, trace::{Span, SpanKind, Tracer, TraceContextExt}, Context, KeyValue};
295    ///
296    /// fn my_function() {
297    ///     let tracer = global::tracer("my-component");
298    ///
299    ///     // Create a parent context
300    ///     let parent = tracer.start("parent-span");
301    ///     let parent_cx = Context::current_with_span(parent);
302    ///
303    ///     // start an active span with explicit parent context and span configuration
304    ///     tracer.in_span_with_builder_and_context(
305    ///         tracer.span_builder("child-span")
306    ///             .with_kind(SpanKind::Client)
307    ///             .with_attributes(vec![KeyValue::new("key", "value")]),
308    ///         &parent_cx,
309    ///         |_cx| {
310    ///             // child span is active here with configured attributes
311    ///         }
312    ///     )
313    /// }
314    /// ```
315    fn in_span_with_builder_and_context<T, F>(
316        &self,
317        builder: SpanBuilder,
318        parent_cx: &Context,
319        f: F,
320    ) -> T
321    where
322        F: FnOnce(Context) -> T,
323        Self::Span: Send + Sync + 'static,
324    {
325        let span = self.build_with_context(builder, parent_cx);
326        let cx = parent_cx.with_span(span);
327        let _guard = cx.clone().attach();
328        f(cx)
329    }
330}
331
332/// `SpanBuilder` allows span attributes to be configured before the span
333/// has started.
334///
335/// ```
336/// use opentelemetry::{
337///     global,
338///     trace::{TracerProvider, SpanBuilder, SpanKind, Tracer},
339/// };
340///
341/// let tracer = global::tracer("example-tracer");
342///
343/// // The builder can be used to create a span directly with the tracer
344/// let _span = tracer.build(SpanBuilder {
345///     name: "example-span-name".into(),
346///     span_kind: Some(SpanKind::Server),
347///     ..Default::default()
348/// });
349///
350/// // Or used with builder pattern
351/// let _span = tracer
352///     .span_builder("example-span-name")
353///     .with_kind(SpanKind::Server)
354///     .start(&tracer);
355/// ```
356#[derive(Clone, Debug, Default)]
357pub struct SpanBuilder {
358    /// Span kind
359    pub span_kind: Option<SpanKind>,
360
361    /// Span name
362    pub name: Cow<'static, str>,
363
364    /// Span start time
365    pub start_time: Option<SystemTime>,
366
367    /// Span attributes that are provided at the span creation time.
368    /// More attributes can be added afterwards.
369    /// Providing duplicate keys will result in multiple attributes
370    /// with the same key, as there is no de-duplication performed.
371    pub attributes: Option<Vec<KeyValue>>,
372
373    /// Span events
374    pub events: Option<Vec<Event>>,
375
376    /// Span Links
377    pub links: Option<Vec<Link>>,
378}
379
380/// SpanBuilder methods
381impl SpanBuilder {
382    /// Create a new span builder from a span name
383    pub fn from_name<T: Into<Cow<'static, str>>>(name: T) -> Self {
384        SpanBuilder {
385            name: name.into(),
386            ..Default::default()
387        }
388    }
389
390    /// Assign span kind
391    pub fn with_kind(self, span_kind: SpanKind) -> Self {
392        SpanBuilder {
393            span_kind: Some(span_kind),
394            ..self
395        }
396    }
397
398    /// Assign span start time
399    pub fn with_start_time<T: Into<SystemTime>>(self, start_time: T) -> Self {
400        SpanBuilder {
401            start_time: Some(start_time.into()),
402            ..self
403        }
404    }
405
406    /// Assign span attributes from an iterable.
407    /// Providing duplicate keys will result in multiple attributes
408    /// with the same key, as there is no de-duplication performed.    
409    pub fn with_attributes<I>(self, attributes: I) -> Self
410    where
411        I: IntoIterator<Item = KeyValue>,
412    {
413        SpanBuilder {
414            attributes: Some(attributes.into_iter().collect()),
415            ..self
416        }
417    }
418
419    /// Assign events
420    pub fn with_events(self, events: Vec<Event>) -> Self {
421        SpanBuilder {
422            events: Some(events),
423            ..self
424        }
425    }
426
427    /// Assign links
428    pub fn with_links(self, mut links: Vec<Link>) -> Self {
429        links.retain(|l| l.span_context.is_valid());
430        SpanBuilder {
431            links: Some(links),
432            ..self
433        }
434    }
435
436    /// Builds a span with the given tracer from this configuration.
437    pub fn start<T: Tracer>(self, tracer: &T) -> T::Span {
438        Context::map_current(|cx| tracer.build_with_context(self, cx))
439    }
440
441    /// Builds a span with the given tracer from this configuration and parent.
442    pub fn start_with_context<T: Tracer>(self, tracer: &T, parent_cx: &Context) -> T::Span {
443        tracer.build_with_context(self, parent_cx)
444    }
445}