opentelemetry/trace/
context.rs

1//! Context extensions for tracing
2use crate::{
3    global, otel_debug,
4    trace::{Span, SpanContext, Status},
5    Context, ContextGuard, KeyValue,
6};
7use std::{borrow::Cow, error::Error, sync::Mutex};
8
9// Re-export for compatability. This used to be contained here.
10pub use crate::context::FutureExt;
11
12const NOOP_SPAN: SynchronizedSpan = SynchronizedSpan {
13    span_context: SpanContext::NONE,
14    inner: None,
15};
16
17/// A reference to the currently active span in this context.
18#[derive(Debug)]
19pub struct SpanRef<'a>(&'a SynchronizedSpan);
20
21#[derive(Debug)]
22pub(crate) struct SynchronizedSpan {
23    /// Immutable span context
24    span_context: SpanContext,
25    /// Mutable span inner that requires synchronization
26    inner: Option<Mutex<global::BoxedSpan>>,
27}
28
29impl SynchronizedSpan {
30    pub(crate) fn span_context(&self) -> &SpanContext {
31        &self.span_context
32    }
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) => {
59                    otel_debug!(
60                        name: "SpanRef.LockFailed",
61                        message = "Failed to acquire lock for SpanRef: {:?}",
62                        reason = format!("{:?}", err),
63                        span_context = format!("{:?}", self.0.span_context));
64                }
65            }
66        }
67    }
68}
69
70impl SpanRef<'_> {
71    /// Record an event in the context this span.
72    ///
73    /// Note that the OpenTelemetry project documents certain "[standard
74    /// attributes]" that have prescribed semantic meanings and are available via
75    /// the [opentelemetry_semantic_conventions] crate.
76    ///
77    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
78    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
79    pub fn add_event<T>(&self, name: T, attributes: Vec<KeyValue>)
80    where
81        T: Into<Cow<'static, str>>,
82    {
83        self.with_inner_mut(|inner| inner.add_event(name, attributes))
84    }
85
86    /// Record an error as an event for this span.
87    ///
88    /// An additional call to [Span::set_status] is required if the status of the
89    /// span should be set to error, as this method does not change the span status.
90    ///
91    /// If this span is not being recorded then this method does nothing.
92    pub fn record_error(&self, err: &dyn Error) {
93        self.with_inner_mut(|inner| inner.record_error(err))
94    }
95
96    /// Record an event with a timestamp in the context this span.
97    ///
98    /// Note that the OpenTelemetry project documents certain "[standard
99    /// attributes]" that have prescribed semantic meanings and are available via
100    /// the [opentelemetry_semantic_conventions] crate.
101    ///
102    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
103    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
104    pub fn add_event_with_timestamp<T>(
105        &self,
106        name: T,
107        timestamp: std::time::SystemTime,
108        attributes: Vec<crate::KeyValue>,
109    ) where
110        T: Into<Cow<'static, str>>,
111    {
112        self.with_inner_mut(move |inner| {
113            inner.add_event_with_timestamp(name, timestamp, attributes)
114        })
115    }
116
117    /// A reference to the [`SpanContext`] for this span.
118    pub fn span_context(&self) -> &SpanContext {
119        &self.0.span_context
120    }
121
122    /// Returns `true` if this span is recording information.
123    ///
124    /// Spans will not be recording information after they have ended.
125    ///
126    /// This flag may be `true` despite the entire trace being sampled out. This
127    /// allows recording and processing of information about the individual
128    /// spans without sending it to the backend. An example of this scenario may
129    /// be recording and processing of all incoming requests for the processing
130    /// and building of SLA/SLO latency charts while sending only a subset -
131    /// sampled spans - to the backend.
132    pub fn is_recording(&self) -> bool {
133        self.0
134            .inner
135            .as_ref()
136            .and_then(|inner| inner.lock().ok().map(|active| active.is_recording()))
137            .unwrap_or(false)
138    }
139
140    /// Set an attribute of this span.
141    ///
142    /// Setting an attribute with the same key as an existing attribute
143    /// generally overwrites the existing attribute's value.
144    ///
145    /// Note that the OpenTelemetry project documents certain "[standard
146    /// attributes]" that have prescribed semantic meanings and are available via
147    /// the [opentelemetry_semantic_conventions] crate.
148    ///
149    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
150    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
151    pub fn set_attribute(&self, attribute: crate::KeyValue) {
152        self.with_inner_mut(move |inner| inner.set_attribute(attribute))
153    }
154
155    /// Set multiple attributes of this span.
156    ///
157    /// Setting an attribute with the same key as an existing attribute
158    /// generally overwrites the existing attribute's value.
159    ///
160    /// Note that the OpenTelemetry project documents certain "[standard
161    /// attributes]" that have prescribed semantic meanings and are available via
162    /// the [opentelemetry_semantic_conventions] crate.
163    ///
164    /// [standard attributes]: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.9.0/specification/trace/semantic_conventions/README.md
165    /// [opentelemetry_semantic_conventions]: https://docs.rs/opentelemetry-semantic-conventions
166    pub fn set_attributes(&self, attributes: impl IntoIterator<Item = KeyValue>) {
167        self.with_inner_mut(move |inner| inner.set_attributes(attributes))
168    }
169
170    /// Sets the status of this `Span`.
171    ///
172    /// If used, this will override the default span status, which is [`Status::Unset`].
173    pub fn set_status(&self, status: Status) {
174        self.with_inner_mut(move |inner| inner.set_status(status))
175    }
176
177    /// Updates the span's name.
178    ///
179    /// After this update, any sampling behavior based on the name will depend on
180    /// the implementation.
181    pub fn update_name<T>(&self, new_name: T)
182    where
183        T: Into<Cow<'static, str>>,
184    {
185        self.with_inner_mut(move |inner| inner.update_name(new_name))
186    }
187
188    /// Signals that the operation described by this span has now ended.
189    pub fn end(&self) {
190        self.end_with_timestamp(crate::time::now());
191    }
192
193    /// Signals that the operation described by this span ended at the given time.
194    pub fn end_with_timestamp(&self, timestamp: std::time::SystemTime) {
195        self.with_inner_mut(move |inner| inner.end_with_timestamp(timestamp))
196    }
197}
198
199/// Methods for storing and retrieving trace data in a [`Context`].
200///
201/// See [`Context`] for examples of setting and retrieving the current context.
202pub trait TraceContextExt {
203    /// Returns a clone of the current context with the included [`Span`].
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
209    ///
210    /// let tracer = global::tracer("example");
211    ///
212    /// // build a span
213    /// let span = tracer.start("parent_span");
214    ///
215    /// // create a new context from the currently active context that includes this span
216    /// let cx = Context::current_with_span(span);
217    ///
218    /// // create a child span by explicitly specifying the parent context
219    /// let child = tracer.start_with_context("child_span", &cx);
220    /// # drop(child)
221    /// ```
222    fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self;
223
224    /// Returns a clone of this context with the included span.
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
230    ///
231    /// fn fn_with_passed_in_context(cx: &Context) {
232    ///     let tracer = global::tracer("example");
233    ///
234    ///     // build a span
235    ///     let span = tracer.start("parent_span");
236    ///
237    ///     // create a new context from the given context that includes the span
238    ///     let cx_with_parent = cx.with_span(span);
239    ///
240    ///     // create a child span by explicitly specifying the parent context
241    ///     let child = tracer.start_with_context("child_span", &cx_with_parent);
242    ///     # drop(child)
243    /// }
244    ///
245    fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self;
246
247    /// Returns a reference to this context's span, or the default no-op span if
248    /// none has been set.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// use opentelemetry::{trace::TraceContextExt, Context};
254    ///
255    /// // Add an event to the currently active span
256    /// Context::map_current(|cx| cx.span().add_event("An event!", vec![]));
257    /// ```
258    fn span(&self) -> SpanRef<'_>;
259
260    /// Returns whether or not an active span has been set.
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// use opentelemetry::{trace::TraceContextExt, Context};
266    ///
267    /// assert!(!Context::map_current(|cx| cx.has_active_span()));
268    /// ```
269    fn has_active_span(&self) -> bool;
270
271    /// Returns a copy of this context with the span context included.
272    ///
273    /// This is useful for building propagators.
274    fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self;
275}
276
277impl TraceContextExt for Context {
278    fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self {
279        Context::current_with_synchronized_span(span.into())
280    }
281
282    fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self {
283        self.with_synchronized_span(span.into())
284    }
285
286    fn span(&self) -> SpanRef<'_> {
287        if let Some(span) = self.span.as_ref() {
288            SpanRef(span)
289        } else {
290            SpanRef(&NOOP_SPAN)
291        }
292    }
293
294    fn has_active_span(&self) -> bool {
295        self.span.is_some()
296    }
297
298    fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {
299        self.with_synchronized_span(span_context.into())
300    }
301}
302
303/// Mark a given `Span` as active.
304///
305/// The `Tracer` MUST provide a way to update its active `Span`, and MAY provide convenience
306/// methods to manage a `Span`'s lifetime and the scope in which a `Span` is active. When an
307/// active `Span` is made inactive, the previously-active `Span` SHOULD be made active. A `Span`
308/// maybe finished (i.e. have a non-null end time) but still be active. A `Span` may be active
309/// on one thread after it has been made inactive on another.
310///
311/// # Examples
312///
313/// ```
314/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
315/// use opentelemetry::trace::{get_active_span, mark_span_as_active};
316///
317/// fn my_function() {
318///     let tracer = global::tracer("my-component-a");
319///     // start an active span in one function
320///     let span = tracer.start("span-name");
321///     let _guard = mark_span_as_active(span);
322///     // anything happening in functions we call can still access the active span...
323///     my_other_function();
324/// }
325///
326/// fn my_other_function() {
327///     // call methods on the current span from
328///     get_active_span(|span| {
329///         span.add_event("An event!".to_string(), vec![KeyValue::new("happened", true)]);
330///     });
331/// }
332/// ```
333#[must_use = "Dropping the guard detaches the context."]
334pub fn mark_span_as_active<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> ContextGuard {
335    let cx = Context::current_with_span(span);
336    cx.attach()
337}
338
339/// Executes a closure with a reference to this thread's current span.
340///
341/// # Examples
342///
343/// ```
344/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
345/// use opentelemetry::trace::get_active_span;
346///
347/// fn my_function() {
348///     // start an active span in one function
349///     global::tracer("my-component").in_span("span-name", |_cx| {
350///         // anything happening in functions we call can still access the active span...
351///         my_other_function();
352///     })
353/// }
354///
355/// fn my_other_function() {
356///     // call methods on the current span from
357///     get_active_span(|span| {
358///         span.add_event("An event!", vec![KeyValue::new("happened", true)]);
359///     })
360/// }
361/// ```
362pub fn get_active_span<F, T>(f: F) -> T
363where
364    F: FnOnce(SpanRef<'_>) -> T,
365{
366    Context::map_current(|cx| f(cx.span()))
367}