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}