opentelemetry/trace/
context.rs

1//! Context extensions for tracing
2use crate::{
3    global,
4    trace::{Span, SpanContext, Status},
5    Context, ContextGuard, KeyValue,
6};
7use futures_core::stream::Stream;
8use futures_sink::Sink;
9use pin_project_lite::pin_project;
10use std::{
11    borrow::Cow,
12    error::Error,
13    pin::Pin,
14    sync::Mutex,
15    task::{Context as TaskContext, Poll},
16};
17
18const NOOP_SPAN: SynchronizedSpan = SynchronizedSpan {
19    span_context: SpanContext::NONE,
20    inner: None,
21};
22
23/// A reference to the currently active span in this context.
24#[derive(Debug)]
25pub struct SpanRef<'a>(&'a SynchronizedSpan);
26
27#[derive(Debug)]
28pub(crate) struct SynchronizedSpan {
29    /// Immutable span context
30    span_context: SpanContext,
31    /// Mutable span inner that requires synchronization
32    inner: Option<Mutex<global::BoxedSpan>>,
33}
34
35impl From<SpanContext> for SynchronizedSpan {
36    fn from(value: SpanContext) -> Self {
37        Self {
38            span_context: value,
39            inner: None,
40        }
41    }
42}
43
44impl<T: Span + Send + Sync + 'static> From<T> for SynchronizedSpan {
45    fn from(value: T) -> Self {
46        Self {
47            span_context: value.span_context().clone(),
48            inner: Some(Mutex::new(global::BoxedSpan::new(value))),
49        }
50    }
51}
52
53impl SpanRef<'_> {
54    fn with_inner_mut<F: FnOnce(&mut global::BoxedSpan)>(&self, f: F) {
55        if let Some(ref inner) = self.0.inner {
56            match inner.lock() {
57                Ok(mut locked) => f(&mut locked),
58                Err(err) => global::handle_error(err),
59            }
60        }
61    }
62}
63
64impl SpanRef<'_> {
65    /// Record an event in the context this span.
66    ///
67    /// Note that the OpenTelemetry project documents certain "[standard
68    /// attributes]" that have prescribed semantic meanings and are available via
69    /// the [opentelemetry_semantic_conventions] crate.
70    ///
71    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
72    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
73    pub fn add_event<T>(&self, name: T, attributes: Vec<KeyValue>)
74    where
75        T: Into<Cow<'static, str>>,
76    {
77        self.with_inner_mut(|inner| inner.add_event(name, attributes))
78    }
79
80    /// Record an error as an event for this span.
81    ///
82    /// An additional call to [Span::set_status] is required if the status of the
83    /// span should be set to error, as this method does not change the span status.
84    ///
85    /// If this span is not being recorded then this method does nothing.
86    pub fn record_error(&self, err: &dyn Error) {
87        self.with_inner_mut(|inner| inner.record_error(err))
88    }
89
90    /// Record an event with a timestamp in the context this span.
91    ///
92    /// Note that the OpenTelemetry project documents certain "[standard
93    /// attributes]" that have prescribed semantic meanings and are available via
94    /// the [opentelemetry_semantic_conventions] crate.
95    ///
96    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
97    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
98    pub fn add_event_with_timestamp<T>(
99        &self,
100        name: T,
101        timestamp: std::time::SystemTime,
102        attributes: Vec<crate::KeyValue>,
103    ) where
104        T: Into<Cow<'static, str>>,
105    {
106        self.with_inner_mut(move |inner| {
107            inner.add_event_with_timestamp(name, timestamp, attributes)
108        })
109    }
110
111    /// A reference to the [`SpanContext`] for this span.
112    pub fn span_context(&self) -> &SpanContext {
113        &self.0.span_context
114    }
115
116    /// Returns `true` if this span is recording information.
117    ///
118    /// Spans will not be recording information after they have ended.
119    ///
120    /// This flag may be `true` despite the entire trace being sampled out. This
121    /// allows recording and processing of information about the individual
122    /// spans without sending it to the backend. An example of this scenario may
123    /// be recording and processing of all incoming requests for the processing
124    /// and building of SLA/SLO latency charts while sending only a subset -
125    /// sampled spans - to the backend.
126    pub fn is_recording(&self) -> bool {
127        self.0
128            .inner
129            .as_ref()
130            .and_then(|inner| inner.lock().ok().map(|active| active.is_recording()))
131            .unwrap_or(false)
132    }
133
134    /// Set an attribute of this span.
135    ///
136    /// Setting an attribute with the same key as an existing attribute
137    /// generally overwrites the existing attribute's value.
138    ///
139    /// Note that the OpenTelemetry project documents certain "[standard
140    /// attributes]" that have prescribed semantic meanings and are available via
141    /// the [opentelemetry_semantic_conventions] crate.
142    ///
143    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
144    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
145    pub fn set_attribute(&self, attribute: crate::KeyValue) {
146        self.with_inner_mut(move |inner| inner.set_attribute(attribute))
147    }
148
149    /// Set multiple attributes of this span.
150    ///
151    /// Setting an attribute with the same key as an existing attribute
152    /// generally overwrites the existing attribute's value.
153    ///
154    /// Note that the OpenTelemetry project documents certain "[standard
155    /// attributes]" that have prescribed semantic meanings and are available via
156    /// the [opentelemetry_semantic_conventions] crate.
157    ///
158    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
159    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
160    pub fn set_attributes(&self, attributes: impl IntoIterator<Item = KeyValue>) {
161        self.with_inner_mut(move |inner| inner.set_attributes(attributes))
162    }
163
164    /// Sets the status of this `Span`.
165    ///
166    /// If used, this will override the default span status, which is [`Status::Unset`].
167    pub fn set_status(&self, status: Status) {
168        self.with_inner_mut(move |inner| inner.set_status(status))
169    }
170
171    /// Updates the span's name.
172    ///
173    /// After this update, any sampling behavior based on the name will depend on
174    /// the implementation.
175    pub fn update_name<T>(&self, new_name: T)
176    where
177        T: Into<Cow<'static, str>>,
178    {
179        self.with_inner_mut(move |inner| inner.update_name(new_name))
180    }
181
182    /// Signals that the operation described by this span has now ended.
183    pub fn end(&self) {
184        self.end_with_timestamp(crate::time::now());
185    }
186
187    /// Signals that the operation described by this span ended at the given time.
188    pub fn end_with_timestamp(&self, timestamp: std::time::SystemTime) {
189        self.with_inner_mut(move |inner| inner.end_with_timestamp(timestamp))
190    }
191}
192
193/// Methods for storing and retrieving trace data in a [`Context`].
194///
195/// See [`Context`] for examples of setting and retrieving the current context.
196pub trait TraceContextExt {
197    /// Returns a clone of the current context with the included [`Span`].
198    ///
199    /// # Examples
200    ///
201    /// ```
202    /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
203    ///
204    /// let tracer = global::tracer("example");
205    ///
206    /// // build a span
207    /// let span = tracer.start("parent_span");
208    ///
209    /// // create a new context from the currently active context that includes this span
210    /// let cx = Context::current_with_span(span);
211    ///
212    /// // create a child span by explicitly specifying the parent context
213    /// let child = tracer.start_with_context("child_span", &cx);
214    /// # drop(child)
215    /// ```
216    fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self;
217
218    /// Returns a clone of this context with the included span.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
224    ///
225    /// fn fn_with_passed_in_context(cx: &Context) {
226    ///     let tracer = global::tracer("example");
227    ///
228    ///     // build a span
229    ///     let span = tracer.start("parent_span");
230    ///
231    ///     // create a new context from the given context that includes the span
232    ///     let cx_with_parent = cx.with_span(span);
233    ///
234    ///     // create a child span by explicitly specifying the parent context
235    ///     let child = tracer.start_with_context("child_span", &cx_with_parent);
236    ///     # drop(child)
237    /// }
238    ///
239    fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self;
240
241    /// Returns a reference to this context's span, or the default no-op span if
242    /// none has been set.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use opentelemetry::{trace::TraceContextExt, Context};
248    ///
249    /// // Add an event to the currently active span
250    /// Context::map_current(|cx| cx.span().add_event("An event!", vec![]));
251    /// ```
252    fn span(&self) -> SpanRef<'_>;
253
254    /// Returns whether or not an active span has been set.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use opentelemetry::{trace::TraceContextExt, Context};
260    ///
261    /// assert!(!Context::map_current(|cx| cx.has_active_span()));
262    /// ```
263    fn has_active_span(&self) -> bool;
264
265    /// Returns a copy of this context with the span context included.
266    ///
267    /// This is useful for building propagators.
268    fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self;
269}
270
271impl TraceContextExt for Context {
272    fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self {
273        Context::current_with_synchronized_span(span.into())
274    }
275
276    fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self {
277        self.with_synchronized_span(span.into())
278    }
279
280    fn span(&self) -> SpanRef<'_> {
281        if let Some(span) = self.span.as_ref() {
282            SpanRef(span)
283        } else {
284            SpanRef(&NOOP_SPAN)
285        }
286    }
287
288    fn has_active_span(&self) -> bool {
289        self.span.is_some()
290    }
291
292    fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {
293        self.with_synchronized_span(span_context.into())
294    }
295}
296
297/// Mark a given `Span` as active.
298///
299/// The `Tracer` MUST provide a way to update its active `Span`, and MAY provide convenience
300/// methods to manage a `Span`'s lifetime and the scope in which a `Span` is active. When an
301/// active `Span` is made inactive, the previously-active `Span` SHOULD be made active. A `Span`
302/// maybe finished (i.e. have a non-null end time) but still be active. A `Span` may be active
303/// on one thread after it has been made inactive on another.
304///
305/// # Examples
306///
307/// ```
308/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
309/// use opentelemetry::trace::{get_active_span, mark_span_as_active};
310///
311/// fn my_function() {
312///     let tracer = global::tracer("my-component-a");
313///     // start an active span in one function
314///     let span = tracer.start("span-name");
315///     let _guard = mark_span_as_active(span);
316///     // anything happening in functions we call can still access the active span...
317///     my_other_function();
318/// }
319///
320/// fn my_other_function() {
321///     // call methods on the current span from
322///     get_active_span(|span| {
323///         span.add_event("An event!".to_string(), vec![KeyValue::new("happened", true)]);
324///     });
325/// }
326/// ```
327#[must_use = "Dropping the guard detaches the context."]
328pub fn mark_span_as_active<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> ContextGuard {
329    let cx = Context::current_with_span(span);
330    cx.attach()
331}
332
333/// Executes a closure with a reference to this thread's current span.
334///
335/// # Examples
336///
337/// ```
338/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
339/// use opentelemetry::trace::get_active_span;
340///
341/// fn my_function() {
342///     // start an active span in one function
343///     global::tracer("my-component").in_span("span-name", |_cx| {
344///         // anything happening in functions we call can still access the active span...
345///         my_other_function();
346///     })
347/// }
348///
349/// fn my_other_function() {
350///     // call methods on the current span from
351///     get_active_span(|span| {
352///         span.add_event("An event!", vec![KeyValue::new("happened", true)]);
353///     })
354/// }
355/// ```
356pub fn get_active_span<F, T>(f: F) -> T
357where
358    F: FnOnce(SpanRef<'_>) -> T,
359{
360    Context::map_current(|cx| f(cx.span()))
361}
362
363pin_project! {
364    /// A future, stream, or sink that has an associated context.
365    #[derive(Clone, Debug)]
366    pub struct WithContext<T> {
367        #[pin]
368        inner: T,
369        otel_cx: Context,
370    }
371}
372
373impl<T: Sized> FutureExt for T {}
374
375impl<T: std::future::Future> std::future::Future for WithContext<T> {
376    type Output = T::Output;
377
378    fn poll(self: Pin<&mut Self>, task_cx: &mut TaskContext<'_>) -> Poll<Self::Output> {
379        let this = self.project();
380        let _guard = this.otel_cx.clone().attach();
381
382        this.inner.poll(task_cx)
383    }
384}
385
386impl<T: Stream> Stream for WithContext<T> {
387    type Item = T::Item;
388
389    fn poll_next(self: Pin<&mut Self>, task_cx: &mut TaskContext<'_>) -> Poll<Option<Self::Item>> {
390        let this = self.project();
391        let _guard = this.otel_cx.clone().attach();
392        T::poll_next(this.inner, task_cx)
393    }
394}
395
396impl<I, T: Sink<I>> Sink<I> for WithContext<T>
397where
398    T: Sink<I>,
399{
400    type Error = T::Error;
401
402    fn poll_ready(
403        self: Pin<&mut Self>,
404        task_cx: &mut TaskContext<'_>,
405    ) -> Poll<Result<(), Self::Error>> {
406        let this = self.project();
407        let _guard = this.otel_cx.clone().attach();
408        T::poll_ready(this.inner, task_cx)
409    }
410
411    fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
412        let this = self.project();
413        let _guard = this.otel_cx.clone().attach();
414        T::start_send(this.inner, item)
415    }
416
417    fn poll_flush(
418        self: Pin<&mut Self>,
419        task_cx: &mut TaskContext<'_>,
420    ) -> Poll<Result<(), Self::Error>> {
421        let this = self.project();
422        let _guard = this.otel_cx.clone().attach();
423        T::poll_flush(this.inner, task_cx)
424    }
425
426    fn poll_close(
427        self: Pin<&mut Self>,
428        task_cx: &mut TaskContext<'_>,
429    ) -> Poll<Result<(), Self::Error>> {
430        let this = self.project();
431        let _enter = this.otel_cx.clone().attach();
432        T::poll_close(this.inner, task_cx)
433    }
434}
435
436/// Extension trait allowing futures, streams, and sinks to be traced with a span.
437pub trait FutureExt: Sized {
438    /// Attaches the provided [`Context`] to this type, returning a `WithContext`
439    /// wrapper.
440    ///
441    /// When the wrapped type is a future, stream, or sink, the attached context
442    /// will be set as current while it is being polled.
443    ///
444    /// [`Context`]: crate::Context
445    fn with_context(self, otel_cx: Context) -> WithContext<Self> {
446        WithContext {
447            inner: self,
448            otel_cx,
449        }
450    }
451
452    /// Attaches the current [`Context`] to this type, returning a `WithContext`
453    /// wrapper.
454    ///
455    /// When the wrapped type is a future, stream, or sink, the attached context
456    /// will be set as the default while it is being polled.
457    ///
458    /// [`Context`]: crate::Context
459    fn with_current_context(self) -> WithContext<Self> {
460        let otel_cx = Context::current();
461        self.with_context(otel_cx)
462    }
463}