tracing/instrument.rs
1use crate::span::Span;
2use core::{
3 future::Future,
4 marker::Sized,
5 mem::ManuallyDrop,
6 pin::Pin,
7 task::{Context, Poll},
8};
9use pin_project_lite::pin_project;
10
11#[cfg(feature = "std")]
12use crate::dispatch::{self, Dispatch};
13
14/// Attaches spans to a [`std::future::Future`].
15///
16/// Extension trait allowing futures to be
17/// instrumented with a `tracing` [span].
18///
19/// [span]: super::Span
20pub trait Instrument: Sized {
21 /// Instruments this type with the provided [`Span`], returning an
22 /// `Instrumented` wrapper.
23 ///
24 /// The attached [`Span`] will be [entered] every time the instrumented
25 /// [`Future`] is polled or [`Drop`]ped.
26 ///
27 /// # Examples
28 ///
29 /// Instrumenting a future:
30 ///
31 /// ```rust
32 /// use tracing::Instrument;
33 ///
34 /// # async fn doc() {
35 /// let my_future = async {
36 /// // ...
37 /// };
38 ///
39 /// my_future
40 /// .instrument(tracing::info_span!("my_future"))
41 /// .await
42 /// # }
43 /// ```
44 ///
45 /// The [`Span::or_current`] combinator can be used in combination with
46 /// `instrument` to ensure that the [current span] is attached to the
47 /// future if the span passed to `instrument` is [disabled]:
48 ///
49 /// ```
50 /// use tracing::Instrument;
51 /// # mod tokio {
52 /// # pub(super) fn spawn(_: impl std::future::Future) {}
53 /// # }
54 ///
55 /// let my_future = async {
56 /// // ...
57 /// };
58 ///
59 /// let outer_span = tracing::info_span!("outer").entered();
60 ///
61 /// // If the "my_future" span is enabled, then the spawned task will
62 /// // be within both "my_future" *and* "outer", since "outer" is
63 /// // "my_future"'s parent. However, if "my_future" is disabled,
64 /// // the spawned task will *not* be in any span.
65 /// tokio::spawn(
66 /// my_future
67 /// .instrument(tracing::debug_span!("my_future"))
68 /// );
69 ///
70 /// // Using `Span::or_current` ensures the spawned task is instrumented
71 /// // with the current span, if the new span passed to `instrument` is
72 /// // not enabled. This means that if the "my_future" span is disabled,
73 /// // the spawned task will still be instrumented with the "outer" span:
74 /// # let my_future = async {};
75 /// tokio::spawn(
76 /// my_future
77 /// .instrument(tracing::debug_span!("my_future").or_current())
78 /// );
79 /// ```
80 ///
81 /// [entered]: super::Span::enter()
82 /// [`Span::or_current`]: super::Span::or_current()
83 /// [current span]: super::Span::current()
84 /// [disabled]: super::Span::is_disabled()
85 /// [`Future`]: std::future::Future
86 fn instrument(self, span: Span) -> Instrumented<Self> {
87 Instrumented {
88 inner: ManuallyDrop::new(self),
89 span,
90 }
91 }
92
93 /// Instruments this type with the [current] [`Span`], returning an
94 /// `Instrumented` wrapper.
95 ///
96 /// The attached [`Span`] will be [entered] every time the instrumented
97 /// [`Future`] is polled or [`Drop`]ped.
98 ///
99 /// This can be used to propagate the current span when spawning a new future.
100 ///
101 /// # Examples
102 ///
103 /// ```rust
104 /// use tracing::Instrument;
105 ///
106 /// # mod tokio {
107 /// # pub(super) fn spawn(_: impl std::future::Future) {}
108 /// # }
109 /// # async fn doc() {
110 /// let span = tracing::info_span!("my_span");
111 /// let _enter = span.enter();
112 ///
113 /// // ...
114 ///
115 /// let future = async {
116 /// tracing::debug!("this event will occur inside `my_span`");
117 /// // ...
118 /// };
119 /// tokio::spawn(future.in_current_span());
120 /// # }
121 /// ```
122 ///
123 /// [current]: super::Span::current()
124 /// [entered]: super::Span::enter()
125 /// [`Span`]: crate::Span
126 /// [`Future`]: std::future::Future
127 #[inline]
128 fn in_current_span(self) -> Instrumented<Self> {
129 self.instrument(Span::current())
130 }
131}
132
133/// Extension trait allowing futures to be instrumented with
134/// a `tracing` collector.
135#[cfg(feature = "std")]
136#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
137pub trait WithCollector: Sized {
138 /// Attaches the provided [collector] to this type, returning a
139 /// [`WithDispatch`] wrapper.
140 ///
141 /// The attached [collector] will be set as the [default] when the returned
142 /// [`Future`] is polled.
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// # pub struct MyCollector;
148 /// # impl tracing::Collect for MyCollector {
149 /// # fn new_span(&self, _: &tracing::span::Attributes) -> tracing::span::Id {
150 /// # tracing::span::Id::from_u64(0)
151 /// # }
152 /// # fn record(&self, _: &tracing::span::Id, _: &tracing::span::Record) {}
153 /// # fn event(&self, _: &tracing::Event<'_>) {}
154 /// # fn record_follows_from(&self, _: &tracing::span::Id, _: &tracing::span::Id) {}
155 /// # fn enabled(&self, _: &tracing::Metadata) -> bool { false }
156 /// # fn enter(&self, _: &tracing::span::Id) {}
157 /// # fn exit(&self, _: &tracing::span::Id) {}
158 /// # fn current_span(&self) -> tracing_core::span::Current {
159 /// # tracing_core::span::Current::unknown()
160 /// # }
161 /// # }
162 /// # impl MyCollector { fn new() -> Self { Self } }
163 /// # async fn docs() {
164 /// use tracing::instrument::WithCollector;
165 ///
166 /// // Set the default collector
167 /// let _default = tracing::collect::set_default(MyCollector::new());
168 ///
169 /// tracing::info!("this event will be recorded by the default collector");
170 ///
171 /// // Create a different collector and attach it to a future.
172 /// let other_collector = MyCollector::new();
173 /// let future = async {
174 /// tracing::info!("this event will be recorded by the other collector");
175 /// // ...
176 /// };
177 ///
178 /// future
179 /// // Attach the other collector to the future before awaiting it
180 /// .with_collector(other_collector)
181 /// .await;
182 ///
183 /// // Once the future has completed, we return to the default collector.
184 /// tracing::info!("this event will be recorded by the default collector");
185 /// # }
186 /// ```
187 ///
188 /// [collector]: super::Collect
189 /// [default]: crate::dispatch#setting-the-default-collector
190 /// [`Future`]: std::future::Future
191 fn with_collector<C>(self, collector: C) -> WithDispatch<Self>
192 where
193 C: Into<Dispatch>,
194 {
195 WithDispatch {
196 inner: self,
197 dispatch: collector.into(),
198 }
199 }
200
201 /// Attaches the current [default] [collector] to this type, returning a
202 /// [`WithDispatch`] wrapper.
203 ///
204 /// The attached collector will be set as the [default] when the returned
205 /// [`Future`] is polled.
206 ///
207 /// This can be used to propagate the current dispatcher context when
208 /// spawning a new future that may run on a different thread.
209 ///
210 /// # Examples
211 ///
212 /// ```
213 /// # mod tokio {
214 /// # pub(super) fn spawn(_: impl std::future::Future) {}
215 /// # }
216 /// # pub struct MyCollector;
217 /// # impl tracing::Collect for MyCollector {
218 /// # fn new_span(&self, _: &tracing::span::Attributes) -> tracing::span::Id {
219 /// # tracing::span::Id::from_u64(0)
220 /// # }
221 /// # fn record(&self, _: &tracing::span::Id, _: &tracing::span::Record) {}
222 /// # fn event(&self, _: &tracing::Event<'_>) {}
223 /// # fn record_follows_from(&self, _: &tracing::span::Id, _: &tracing::span::Id) {}
224 /// # fn enabled(&self, _: &tracing::Metadata) -> bool { false }
225 /// # fn enter(&self, _: &tracing::span::Id) {}
226 /// # fn exit(&self, _: &tracing::span::Id) {}
227 /// # fn current_span(&self) -> tracing_core::span::Current {
228 /// # tracing_core::span::Current::unknown()
229 /// # }
230 /// # }
231 /// # impl MyCollector { fn new() -> Self { Self } }
232 /// # async fn docs() {
233 /// use tracing::instrument::WithCollector;
234 ///
235 /// // Using `set_default` (rather than `set_global_default`) sets the
236 /// // default collector for *this* thread only.
237 /// let _default = tracing::collect::set_default(MyCollector::new());
238 ///
239 /// let future = async {
240 /// // ...
241 /// };
242 ///
243 /// // If a multi-threaded async runtime is in use, this spawned task may
244 /// // run on a different thread, in a different default collector's context.
245 /// tokio::spawn(future);
246 ///
247 /// // However, calling `with_current_collector` on the future before
248 /// // spawning it, ensures that the current thread's default collector is
249 /// // propagated to the spawned task, regardless of where it executes:
250 /// # let future = async { };
251 /// tokio::spawn(future.with_current_collector());
252 /// # }
253 /// ```
254 /// [collector]: super::Collect
255 /// [default]: crate::dispatch#setting-the-default-collector
256 /// [`Future`]: std::future::Future
257 #[inline]
258 fn with_current_collector(self) -> WithDispatch<Self> {
259 WithDispatch {
260 inner: self,
261 dispatch: dispatch::get_default(|default| default.clone()),
262 }
263 }
264}
265
266#[cfg(feature = "std")]
267pin_project! {
268 /// A [`Future`] that has been instrumented with a `tracing` [collector].
269 ///
270 /// This type is returned by the [`WithCollector`] extension trait. See that
271 /// trait's documentation for details.
272 ///
273 /// [`Future`]: std::future::Future
274 /// [collector]: crate::Collector
275 #[derive(Clone, Debug)]
276 #[must_use = "futures do nothing unless you `.await` or poll them"]
277 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
278 pub struct WithDispatch<T> {
279 #[pin]
280 inner: T,
281 dispatch: Dispatch,
282 }
283}
284
285pin_project! {
286 /// A [`Future`] that has been instrumented with a `tracing` [`Span`].
287 ///
288 /// This type is returned by the [`Instrument`] extension trait. See that
289 /// trait's documentation for details.
290 ///
291 /// [`Future`]: std::future::Future
292 /// [`Span`]: crate::Span
293 #[project = InstrumentedProj]
294 #[project_ref = InstrumentedProjRef]
295 #[derive(Debug, Clone)]
296 #[must_use = "futures do nothing unless you `.await` or poll them"]
297 pub struct Instrumented<T> {
298 // `ManuallyDrop` is used here to to enter instrument `Drop` by entering
299 // `Span` and executing `ManuallyDrop::drop`.
300 #[pin]
301 inner: ManuallyDrop<T>,
302 span: Span,
303 }
304
305 impl<T> PinnedDrop for Instrumented<T> {
306 fn drop(this: Pin<&mut Self>) {
307 let this = this.project();
308 let _enter = this.span.enter();
309 // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't
310 // different from wrapping `T` in `Option` and calling
311 // `Pin::set(&mut this.inner, None)`, except avoiding
312 // additional memory overhead.
313 // 2. `ManuallyDrop::drop()` is safe, because
314 // `PinnedDrop::drop()` is guaranteed to be called only
315 // once.
316 unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) }
317 }
318 }
319}
320
321impl<'a, T> InstrumentedProj<'a, T> {
322 /// Get a mutable reference to the [`Span`] a pinned mutable reference to
323 /// the wrapped type.
324 fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) {
325 // SAFETY: As long as `ManuallyDrop<T>` does not move, `T` won't move
326 // and `inner` is valid, because `ManuallyDrop::drop` is called
327 // only inside `Drop` of the `Instrumented`.
328 let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) };
329 (self.span, inner)
330 }
331}
332
333impl<'a, T> InstrumentedProjRef<'a, T> {
334 /// Get a reference to the [`Span`] a pinned reference to the wrapped type.
335 fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) {
336 // SAFETY: As long as `ManuallyDrop<T>` does not move, `T` won't move
337 // and `inner` is valid, because `ManuallyDrop::drop` is called
338 // only inside `Drop` of the `Instrumented`.
339 let inner = unsafe { self.inner.map_unchecked(|v| &**v) };
340 (self.span, inner)
341 }
342}
343
344// === impl Instrumented ===
345
346impl<T: Future> Future for Instrumented<T> {
347 type Output = T::Output;
348
349 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
350 let (span, inner) = self.project().span_and_inner_pin_mut();
351 let _enter = span.enter();
352 inner.poll(cx)
353 }
354}
355
356impl<T: Sized> Instrument for T {}
357
358impl<T> Instrumented<T> {
359 /// Borrows the `Span` that this type is instrumented by.
360 pub fn span(&self) -> &Span {
361 &self.span
362 }
363
364 /// Mutably borrows the `Span` that this type is instrumented by.
365 pub fn span_mut(&mut self) -> &mut Span {
366 &mut self.span
367 }
368
369 /// Borrows the wrapped type.
370 pub fn inner(&self) -> &T {
371 &self.inner
372 }
373
374 /// Mutably borrows the wrapped type.
375 pub fn inner_mut(&mut self) -> &mut T {
376 &mut self.inner
377 }
378
379 /// Get a pinned reference to the wrapped type.
380 pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> {
381 self.project_ref().span_and_inner_pin_ref().1
382 }
383
384 /// Get a pinned mutable reference to the wrapped type.
385 pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
386 self.project().span_and_inner_pin_mut().1
387 }
388
389 /// Consumes the `Instrumented`, returning the wrapped type.
390 ///
391 /// Note that this drops the span.
392 pub fn into_inner(self) -> T {
393 // To manually destructure `Instrumented` without `Drop`, we
394 // move it into a ManuallyDrop and use pointers to its fields
395 let this = ManuallyDrop::new(self);
396 let span: *const Span = &this.span;
397 let inner: *const ManuallyDrop<T> = &this.inner;
398 // SAFETY: Those pointers are valid for reads, because `Drop` didn't
399 // run, and properly aligned, because `Instrumented` isn't
400 // `#[repr(packed)]`.
401 let _span = unsafe { span.read() };
402 let inner = unsafe { inner.read() };
403 ManuallyDrop::into_inner(inner)
404 }
405}
406
407// === impl WithDispatch ===
408
409#[cfg(feature = "std")]
410#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
411impl<T: Future> Future for WithDispatch<T> {
412 type Output = T::Output;
413
414 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
415 let this = self.project();
416 let dispatch = this.dispatch;
417 let future = this.inner;
418 let _default = dispatch::set_default(dispatch);
419 future.poll(cx)
420 }
421}
422
423#[cfg(feature = "std")]
424#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
425impl<T: Sized> WithCollector for T {}
426
427#[cfg(feature = "std")]
428#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
429impl<T> WithDispatch<T> {
430 /// Borrows the [`Dispatch`] that is entered when this type is polled.
431 pub fn dispatch(&self) -> &Dispatch {
432 &self.dispatch
433 }
434
435 /// Borrows the wrapped type.
436 pub fn inner(&self) -> &T {
437 &self.inner
438 }
439
440 /// Mutably borrows the wrapped type.
441 pub fn inner_mut(&mut self) -> &mut T {
442 &mut self.inner
443 }
444
445 /// Get a pinned reference to the wrapped type.
446 pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> {
447 self.project_ref().inner
448 }
449
450 /// Get a pinned mutable reference to the wrapped type.
451 pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
452 self.project().inner
453 }
454
455 /// Consumes the `Instrumented`, returning the wrapped type.
456 ///
457 /// Note that this drops the span.
458 pub fn into_inner(self) -> T {
459 self.inner
460 }
461}