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, WithContext};
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 /// Adds a [`Link`] to another [`SpanContext`].
189 ///
190 /// This method allows linking the current span to another span, identified by
191 /// its `SpanContext`. Links can be used to connect spans from different traces
192 /// or within the same trace. Attributes can be attached to the link to provide
193 /// additional context or metadata.
194 ///
195 /// # Arguments
196 ///
197 /// * `span_context` - The `SpanContext` of the span to link to. This represents
198 /// the target span's unique identifiers and trace information.
199 /// * `attributes` - A vector of `KeyValue` pairs that describe additional
200 /// attributes of the link. These attributes can include any contextual
201 /// information relevant to the link between the spans.
202 ///
203 /// Note - Any [`Link`] added via this mechanism is not accessible to a `Sampler`.
204 /// It is recommended to add Links at [`Span`] creation time, rather than adding
205 /// them afterwards.
206 ///
207 /// [`Link`]: crate::trace::Link
208 pub fn add_link(&self, span_context: SpanContext, attributes: Vec<KeyValue>) {
209 self.with_inner_mut(move |inner| inner.add_link(span_context, attributes));
210 }
211
212 /// Signals that the operation described by this span has now ended.
213 pub fn end(&self) {
214 self.end_with_timestamp(crate::time::now());
215 }
216
217 /// Signals that the operation described by this span ended at the given time.
218 pub fn end_with_timestamp(&self, timestamp: std::time::SystemTime) {
219 self.with_inner_mut(move |inner| inner.end_with_timestamp(timestamp))
220 }
221}
222
223/// Methods for storing and retrieving trace data in a [`Context`].
224///
225/// See [`Context`] for examples of setting and retrieving the current context.
226pub trait TraceContextExt {
227 /// Returns a clone of the current context with the included [`Span`].
228 ///
229 /// # Examples
230 ///
231 /// ```
232 /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
233 ///
234 /// let tracer = global::tracer("example");
235 ///
236 /// // build a span
237 /// let span = tracer.start("parent_span");
238 ///
239 /// // create a new context from the currently active context that includes this span
240 /// let cx = Context::current_with_span(span);
241 ///
242 /// // create a child span by explicitly specifying the parent context
243 /// let child = tracer.start_with_context("child_span", &cx);
244 /// # drop(child)
245 /// ```
246 fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self;
247
248 /// Returns a clone of this context with the included span.
249 ///
250 /// # Examples
251 ///
252 /// ```
253 /// use opentelemetry::{global, trace::{TraceContextExt, Tracer}, Context};
254 ///
255 /// fn fn_with_passed_in_context(cx: &Context) {
256 /// let tracer = global::tracer("example");
257 ///
258 /// // build a span
259 /// let span = tracer.start("parent_span");
260 ///
261 /// // create a new context from the given context that includes the span
262 /// let cx_with_parent = cx.with_span(span);
263 ///
264 /// // create a child span by explicitly specifying the parent context
265 /// let child = tracer.start_with_context("child_span", &cx_with_parent);
266 /// # drop(child)
267 /// }
268 ///
269 fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self;
270
271 /// Returns a reference to this context's span, or the default no-op span if
272 /// none has been set.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use opentelemetry::{trace::TraceContextExt, Context};
278 ///
279 /// // Add an event to the currently active span
280 /// Context::map_current(|cx| cx.span().add_event("An event!", vec![]));
281 /// ```
282 fn span(&self) -> SpanRef<'_>;
283
284 /// Returns whether or not an active span has been set.
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// use opentelemetry::{trace::TraceContextExt, Context};
290 ///
291 /// assert!(!Context::map_current(|cx| cx.has_active_span()));
292 /// ```
293 fn has_active_span(&self) -> bool;
294
295 /// Returns a copy of this context with the span context included.
296 ///
297 /// This is useful for building propagators.
298 fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self;
299}
300
301impl TraceContextExt for Context {
302 fn current_with_span<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> Self {
303 Context::current_with_synchronized_span(span.into())
304 }
305
306 fn with_span<T: crate::trace::Span + Send + Sync + 'static>(&self, span: T) -> Self {
307 self.with_synchronized_span(span.into())
308 }
309
310 fn span(&self) -> SpanRef<'_> {
311 if let Some(span) = self.span.as_ref() {
312 SpanRef(span)
313 } else {
314 SpanRef(&NOOP_SPAN)
315 }
316 }
317
318 fn has_active_span(&self) -> bool {
319 self.span.is_some()
320 }
321
322 fn with_remote_span_context(&self, span_context: crate::trace::SpanContext) -> Self {
323 self.with_synchronized_span(span_context.into())
324 }
325}
326
327/// Mark a given `Span` as active.
328///
329/// The `Tracer` MUST provide a way to update its active `Span`, and MAY provide convenience
330/// methods to manage a `Span`'s lifetime and the scope in which a `Span` is active. When an
331/// active `Span` is made inactive, the previously-active `Span` SHOULD be made active. A `Span`
332/// maybe finished (i.e. have a non-null end time) but still be active. A `Span` may be active
333/// on one thread after it has been made inactive on another.
334///
335/// # Examples
336///
337/// ```
338/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
339/// use opentelemetry::trace::{get_active_span, mark_span_as_active};
340///
341/// fn my_function() {
342/// let tracer = global::tracer("my-component-a");
343/// // start an active span in one function
344/// let span = tracer.start("span-name");
345/// let _guard = mark_span_as_active(span);
346/// // anything happening in functions we call can still access the active span...
347/// my_other_function();
348/// }
349///
350/// fn my_other_function() {
351/// // call methods on the current span from
352/// get_active_span(|span| {
353/// span.add_event("An event!".to_string(), vec![KeyValue::new("happened", true)]);
354/// });
355/// }
356/// ```
357#[must_use = "Dropping the guard detaches the context."]
358pub fn mark_span_as_active<T: crate::trace::Span + Send + Sync + 'static>(span: T) -> ContextGuard {
359 let cx = Context::current_with_span(span);
360 cx.attach()
361}
362
363/// Executes a closure with a reference to this thread's current span.
364///
365/// # Examples
366///
367/// ```
368/// use opentelemetry::{global, trace::{Span, Tracer}, KeyValue};
369/// use opentelemetry::trace::get_active_span;
370///
371/// fn my_function() {
372/// // start an active span in one function
373/// global::tracer("my-component").in_span("span-name", |_cx| {
374/// // anything happening in functions we call can still access the active span...
375/// my_other_function();
376/// })
377/// }
378///
379/// fn my_other_function() {
380/// // call methods on the current span from
381/// get_active_span(|span| {
382/// span.add_event("An event!", vec![KeyValue::new("happened", true)]);
383/// })
384/// }
385/// ```
386pub fn get_active_span<F, T>(f: F) -> T
387where
388 F: FnOnce(SpanRef<'_>) -> T,
389{
390 Context::map_current(|cx| f(cx.span()))
391}