Skip to main content

hyper_util/rt/
tracing.rs

1//! Runtime components for use with [`tracing`].
2//!
3//! This module provides [`Executor`] implementations that configure
4//! instrumentation of spawned futures. These [`Executor`]s can propagate
5//! tracing [`Span`]s to futures spawned onto the async runtime. See the
6//! crate-level documentation of [`tracing`] for [more information] about spans.
7//!
8//! # Choosing an [`Executor`].
9//!
10//! Hyper spawns [`Future`]s onto an [`Executor`], to avoid tightly coupling
11//! APIs to any particular async runtime. This includes background tasks that
12//! might help service I/O for the lifetime of a connection, for example.
13//!
14//! Some [`Subscriber`][tracing::subscriber] implementations have different
15//! semantics regarding the lifecycle of [`Span`]s. Integrations with
16//! OpenTelemetry collectors, for example, might not emit the events within
17//! the context of a span until it is closed. Conversely, subscribers that
18//! print traces to the terminal may not have to contend with these details when
19//! instrumenting long-lived tasks that run in the background.
20//!
21//! This module provides different executors to help pass tracing context in
22//! the manner appropriate for your application. For most typical applications,
23//! [`CurrentSpanExecutor<E>`] should suffice.
24//!
25//! # Examples
26//!
27//! Run spawned tasks within a provided span.
28//!
29//! ```
30//! # #[cfg(feature = "tokio")]
31//! # {
32//! use hyper_util::rt::{TokioExecutor, WithSpanExecutor};
33//!
34//! let span = tracing::info_span!("example");
35//! let executor = WithSpanExecutor::new(TokioExecutor::new(), span);
36//! # }
37//! ```
38//!
39//! Run spawned tasks within the current span when [`Executor::execute()`] is
40//! called.
41//!
42//! ```
43//! # #[cfg(feature = "tokio")]
44//! # {
45//! use hyper_util::rt::{TokioExecutor, CurrentSpanExecutor};
46//!
47//! let executor = CurrentSpanExecutor::new(TokioExecutor::new());
48//! # }
49//! ```
50//!
51//! Run spawned tasks within distinct spans that are marked as following from
52//! the active span when [`Executor::execute()`] is called.
53//!
54//! ```
55//! # #[cfg(feature = "tokio")]
56//! # {
57//! use hyper_util::rt::{MkSpanExecutor, TokioExecutor};
58//! use tracing::{info_span, Span};
59//!
60//! let mk = || {
61//!     let span = info_span!("example");
62//!     span.follows_from(Span::current());
63//!     span
64//! };
65//! let executor = MkSpanExecutor::new(TokioExecutor::new(), mk);
66//! # }
67//! ```
68//!
69//! [more information]: tracing#spans-1
70
71use hyper::rt::Executor;
72use tracing::{
73    Span,
74    instrument::{Instrument, Instrumented},
75};
76
77/// An executor that propagates the current tracing span to its futures.
78///
79/// The span is captured when [`execute`](Executor::execute) is called, and is
80/// entered each time the future is polled or dropped. Execution is delegated to
81/// the wrapped executor, without requiring a particular runtime.
82///
83/// Requires the `tracing` feature.
84///
85/// # Example
86///
87/// ```
88/// # #[cfg(feature = "tokio")]
89/// # {
90/// use hyper_util::rt::{TokioExecutor, CurrentSpanExecutor};
91///
92/// let executor = CurrentSpanExecutor::new(TokioExecutor::new());
93/// # }
94/// ```
95#[derive(Clone, Copy, Debug, Default)]
96pub struct CurrentSpanExecutor<E> {
97    inner: E,
98}
99
100/// An executor that propagates a provided tracing span to its futures.
101///
102/// The span provided to this executor is entered each time the future is
103/// polled or dropped. Execution is delegated to the wrapped executor, without
104/// requiring a particular runtime.
105///
106/// Requires the `tracing` feature.
107///
108/// # Example
109///
110/// ```
111/// # #[cfg(feature = "tokio")]
112/// # {
113/// use hyper_util::rt::{TokioExecutor, WithSpanExecutor};
114///
115/// let span = tracing::info_span!("example");
116/// let executor = WithSpanExecutor::new(TokioExecutor::new(), span);
117/// # }
118/// ```
119#[derive(Clone, Debug)]
120pub struct WithSpanExecutor<E> {
121    inner: E,
122    span: Span,
123}
124
125/// An executor that uses a callback to propagate tracing span to its futures.
126///
127/// The callback is invoked each time a future is spawned, creating a span that
128/// will be entered each time that future is polled or dropped. Execution is
129/// delegated to the wrapped executor, without requiring a particular runtime.
130///
131/// Requires the `tracing` feature.
132///
133/// # Example
134///
135/// Spawned tasks can be marked as "following from" the execution context.
136///
137/// See [`tracing::Span::follows_from()`] for more information about
138/// indicating causal relationships between spans.
139///
140/// ```
141/// # #[cfg(feature = "tokio")]
142/// # {
143/// use hyper_util::rt::{MkSpanExecutor, TokioExecutor};
144/// use tracing::{info_span, Span};
145///
146/// let mk = || {
147///     let span = info_span!("example");
148///     span.follows_from(Span::current());
149///     span
150/// };
151/// let executor = MkSpanExecutor::new(TokioExecutor::new(), mk);
152/// # }
153/// ```
154///
155/// Spawned tasks can be marked as children of the execution context.
156///
157/// ```
158/// # #[cfg(feature = "tokio")]
159/// # {
160/// use hyper_util::rt::{MkSpanExecutor, TokioExecutor};
161/// use tracing::{info_span, Span};
162///
163/// let mk = || info_span!(parent: Span::current(), "example");
164/// let executor = MkSpanExecutor::new(TokioExecutor::new(), mk);
165/// # }
166/// ```
167#[derive(Clone, Debug)]
168pub struct MkSpanExecutor<E, F> {
169    inner: E,
170    mk: F,
171}
172
173// ===== impl CurrentSpanExecutor =====
174
175impl<E> CurrentSpanExecutor<E> {
176    /// Wrap an executor to propagate the current tracing span to its futures.
177    pub fn new(inner: E) -> Self {
178        Self { inner }
179    }
180}
181
182impl<E, F> Executor<F> for CurrentSpanExecutor<E>
183where
184    E: Executor<Instrumented<F>>,
185    F: Future,
186{
187    fn execute(&self, future: F) {
188        self.inner.execute(future.in_current_span());
189    }
190}
191
192// ===== impl WithSpanExecutor =====
193
194impl<E> WithSpanExecutor<E> {
195    /// Wrap an executor to propagate the provided tracing span to its futures.
196    pub fn new(inner: E, span: Span) -> Self {
197        Self { inner, span }
198    }
199
200    /// Wrap an executor to propagate the current tracing span to its futures.
201    ///
202    /// This will instrument futures with the span that is active at the call-site of _this_
203    /// function. Use [`CurrentSpanExecutor<E>`] if you would prefer to propagate the current span
204    /// when [`Executor::execute()`] is called, rather than span that is active when initializating
205    /// the executor.
206    pub fn current(inner: E) -> Self {
207        Self {
208            inner,
209            span: Span::current(),
210        }
211    }
212}
213
214impl<E, F> Executor<F> for WithSpanExecutor<E>
215where
216    E: Executor<Instrumented<F>>,
217    F: Future,
218{
219    fn execute(&self, future: F) {
220        self.inner.execute(future.instrument(self.span.clone()));
221    }
222}
223
224// ===== impl MkSpanExecutor =====
225
226impl<E, F> MkSpanExecutor<E, F> {
227    /// Wrap an executor that creates new spans to instrument spawned futures.
228    pub fn new(inner: E, mk: F) -> Self {
229        Self { inner, mk }
230    }
231}
232
233impl<E, F, Fut> Executor<Fut> for MkSpanExecutor<E, F>
234where
235    E: Executor<Instrumented<Fut>>,
236    F: Fn() -> Span,
237    Fut: Future,
238{
239    fn execute(&self, future: Fut) {
240        let span = (self.mk)();
241        self.inner.execute(future.instrument(span));
242    }
243}
244
245#[cfg(test)]
246mod tests {
247    use super::{CurrentSpanExecutor, MkSpanExecutor, WithSpanExecutor};
248    use hyper::rt::Executor;
249    use std::{
250        cell::RefCell,
251        future::poll_fn,
252        pin::Pin,
253        sync::{Arc, Mutex},
254        task::Poll,
255    };
256
257    #[derive(Default)]
258    struct DeferredExecutor<'a> {
259        future: RefCell<Option<Pin<Box<dyn Future<Output = ()> + 'a>>>>,
260    }
261
262    impl<'a, F: Future<Output = ()> + 'a> Executor<F> for &DeferredExecutor<'a> {
263        fn execute(&self, future: F) {
264            *self.future.borrow_mut() = Some(Box::pin(future));
265        }
266    }
267
268    #[test]
269    fn current_span_executor_propagates_span_from_execute_on_each_poll() {
270        let _subscriber = tracing::subscriber::set_default(tracing_subscriber::registry());
271        let construction_span = tracing::info_span!("construction");
272        let execution_span = tracing::info_span!("execution");
273        let polling_span = tracing::info_span!("polling");
274        assert!(execution_span.id().is_some());
275
276        // Borrowing a local executor and future also checks that the wrapper
277        // does not impose Send or 'static bounds on the inner executor.
278        let polls = RefCell::new(0);
279        let inner = DeferredExecutor::default();
280        let executor = construction_span.in_scope(|| CurrentSpanExecutor::new(&inner));
281        execution_span.in_scope(|| {
282            executor.execute(poll_fn(|_| {
283                assert_eq!(tracing::Span::current().id(), execution_span.id());
284                *polls.borrow_mut() += 1;
285                if *polls.borrow() == 1 {
286                    Poll::Pending
287                } else {
288                    Poll::Ready(())
289                }
290            }));
291        });
292
293        let _entered = polling_span.enter();
294        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
295        assert!(task.poll().is_pending());
296        assert_eq!(tracing::Span::current().id(), polling_span.id());
297        assert!(task.poll().is_ready());
298        assert_eq!(tracing::Span::current().id(), polling_span.id());
299        assert_eq!(*polls.borrow(), 2);
300    }
301
302    #[test]
303    fn with_span_executor_propagates_given_span_on_each_poll() {
304        let _subscriber = tracing::subscriber::set_default(tracing_subscriber::registry());
305        let construction_span = tracing::info_span!("construction");
306        let execution_span = tracing::info_span!("execution");
307        let polling_span = tracing::info_span!("polling");
308        let with_span = tracing::info_span!("with");
309        assert!(execution_span.id().is_some());
310
311        // Borrowing a local executor and future also checks that the wrapper
312        // does not impose Send or 'static bounds on the inner executor.
313        let polls = RefCell::new(0);
314        let inner = DeferredExecutor::default();
315        let executor =
316            construction_span.in_scope(|| WithSpanExecutor::new(&inner, with_span.clone()));
317        execution_span.in_scope(|| {
318            executor.execute(poll_fn(|_| {
319                // Execution happens within the given span.
320                assert_eq!(tracing::Span::current().id(), with_span.id());
321                *polls.borrow_mut() += 1;
322                if *polls.borrow() == 1 {
323                    Poll::Pending
324                } else {
325                    Poll::Ready(())
326                }
327            }));
328        });
329
330        let _entered = polling_span.enter();
331        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
332        assert!(task.poll().is_pending());
333        assert_eq!(tracing::Span::current().id(), polling_span.id());
334        assert!(task.poll().is_ready());
335        assert_eq!(tracing::Span::current().id(), polling_span.id());
336        assert_eq!(*polls.borrow(), 2);
337    }
338
339    #[test]
340    fn with_span_executor_current_propagates_construction_span() {
341        let _subscriber = tracing::subscriber::set_default(tracing_subscriber::registry());
342        let construction_span = tracing::info_span!("construction");
343        let execution_span = tracing::info_span!("execution");
344        let polling_span = tracing::info_span!("polling");
345        assert!(execution_span.id().is_some());
346
347        // Borrowing a local executor and future also checks that the wrapper
348        // does not impose Send or 'static bounds on the inner executor.
349        let polls = RefCell::new(0);
350        let inner = DeferredExecutor::default();
351        let executor = construction_span.in_scope(|| WithSpanExecutor::current(&inner));
352        execution_span.in_scope(|| {
353            executor.execute(poll_fn(|_| {
354                // Execution happens within the given span.
355                assert_eq!(tracing::Span::current().id(), construction_span.id());
356                *polls.borrow_mut() += 1;
357                if *polls.borrow() == 1 {
358                    Poll::Pending
359                } else {
360                    Poll::Ready(())
361                }
362            }));
363        });
364
365        let _entered = polling_span.enter();
366        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
367        assert!(task.poll().is_pending());
368        assert_eq!(tracing::Span::current().id(), polling_span.id());
369        assert!(task.poll().is_ready());
370        assert_eq!(tracing::Span::current().id(), polling_span.id());
371        assert_eq!(*polls.borrow(), 2);
372    }
373
374    #[test]
375    fn mk_span_executor_current_propagates_child_span() {
376        let _subscriber = tracing::subscriber::set_default(tracing_subscriber::registry());
377        let construction_span = tracing::info_span!("construction");
378        let execution_span = tracing::info_span!("execution");
379        let polling_span = tracing::info_span!("polling");
380        assert!(execution_span.id().is_some());
381
382        // A callback that creates a new child of the given span.
383        let mk = || tracing::info_span!(parent: tracing::Span::current(), "child");
384
385        // Borrowing a local executor and future also checks that the wrapper
386        // does not impose Send or 'static bounds on the inner executor.
387        let polls = RefCell::new(0);
388        let inner = DeferredExecutor::default();
389        let executor = construction_span.in_scope(|| MkSpanExecutor::new(&inner, mk));
390        execution_span.in_scope(|| {
391            executor.execute(poll_fn(|_| {
392                // Execution happens within the created child span.
393                let span = tracing::Span::current();
394                assert_eq!(span.metadata().unwrap().name(), "child");
395                *polls.borrow_mut() += 1;
396                if *polls.borrow() == 1 {
397                    Poll::Pending
398                } else {
399                    Poll::Ready(())
400                }
401            }));
402        });
403
404        let _entered = polling_span.enter();
405        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
406        assert!(task.poll().is_pending());
407        assert_eq!(tracing::Span::current().id(), polling_span.id());
408        assert!(task.poll().is_ready());
409        assert_eq!(tracing::Span::current().id(), polling_span.id());
410        assert_eq!(*polls.borrow(), 2);
411    }
412
413    /// A subscriber that records causal `follows_from` relationships.
414    struct FollowsFromSubscriber<S> {
415        inner: S,
416        follows_from: Arc<Mutex<Vec<FollowsFrom>>>,
417    }
418
419    /// A tuple representing a causal relationship between two spans.
420    ///
421    /// This means that the span with the former id followed from the span with the latter id.
422    type FollowsFrom = (tracing::span::Id, tracing::span::Id);
423
424    impl<S> FollowsFromSubscriber<S> {
425        fn new(inner: S) -> Self {
426            Self {
427                inner,
428                follows_from: Default::default(),
429            }
430        }
431
432        /// Returns a reference to the set of relationships observed.
433        fn follows_from(&self) -> Arc<Mutex<Vec<FollowsFrom>>> {
434            Arc::clone(&self.follows_from)
435        }
436    }
437
438    impl<S> tracing::Subscriber for FollowsFromSubscriber<S>
439    where
440        S: tracing::Subscriber,
441    {
442        fn record_follows_from(&self, span: &tracing::span::Id, follows: &tracing::span::Id) {
443            self.follows_from
444                .lock()
445                .unwrap()
446                .push((span.clone(), follows.clone()));
447            self.inner.record_follows_from(span, follows);
448        }
449
450        fn current_span(&self) -> tracing_core::span::Current {
451            self.inner.current_span()
452        }
453
454        // Other methods delegate to `inner`...
455
456        fn enabled(&self, metadata: &tracing::Metadata<'_>) -> bool {
457            self.inner.enabled(metadata)
458        }
459
460        fn enter(&self, span: &tracing::span::Id) {
461            self.inner.enter(span);
462        }
463
464        fn event(&self, event: &tracing::Event<'_>) {
465            self.inner.event(event);
466        }
467
468        fn exit(&self, span: &tracing::span::Id) {
469            self.inner.exit(span);
470        }
471
472        fn new_span(&self, span: &tracing::span::Attributes<'_>) -> tracing::span::Id {
473            self.inner.new_span(span)
474        }
475
476        fn record(&self, span: &tracing::span::Id, values: &tracing::span::Record<'_>) {
477            self.inner.record(span, values);
478        }
479    }
480
481    #[test]
482    fn mk_span_executor_current_propagates_causal_span_relationships() {
483        // Use a subscriber that records `follows_from` relationships.
484        let subscriber = FollowsFromSubscriber::new(tracing_subscriber::registry());
485        let relationships = subscriber.follows_from();
486        let _subscriber = tracing::subscriber::set_default(subscriber);
487
488        let construction_span = tracing::info_span!("construction");
489        let execution_a_span = tracing::info_span!("execution_a");
490        let execution_b_span = tracing::info_span!("execution_b");
491        let polling_span = tracing::info_span!("polling");
492
493        // A callback that creates a span that `follows_from` the execution span.
494        let mk = || {
495            let span = tracing::info_span!("spawned");
496            span.follows_from(tracing::Span::current());
497            span
498        };
499
500        let polls = RefCell::new(0);
501        let inner = DeferredExecutor::default();
502        let executor = construction_span.in_scope(|| MkSpanExecutor::new(&inner, mk));
503        execution_a_span.in_scope(|| {
504            executor.execute(poll_fn(|_| {
505                // Execution happens within the created child span.
506                let span = tracing::Span::current();
507                assert_eq!(span.metadata().unwrap().name(), "spawned");
508                *polls.borrow_mut() += 1;
509                if *polls.borrow() == 1 {
510                    Poll::Pending
511                } else {
512                    Poll::Ready(())
513                }
514            }));
515        });
516
517        let _entered = polling_span.enter();
518        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
519        assert!(task.poll().is_pending());
520        assert_eq!(tracing::Span::current().id(), polling_span.id());
521        assert_eq!(relationships.lock().unwrap().len(), 1);
522        assert!(task.poll().is_ready());
523        assert_eq!(tracing::Span::current().id(), polling_span.id());
524        assert_eq!(*polls.borrow(), 2);
525        assert_eq!(relationships.lock().unwrap().len(), 1);
526
527        execution_b_span.in_scope(|| {
528            executor.execute(poll_fn(|_| {
529                let span = tracing::Span::current();
530                assert_eq!(span.metadata().unwrap().name(), "spawned");
531                Poll::Ready(())
532            }));
533        });
534
535        let _entered = polling_span.enter();
536        let mut task = tokio_test::task::spawn(inner.future.borrow_mut().take().unwrap());
537        assert!(task.poll().is_ready());
538
539        // The first task followed from the `execution_a` span. The second task
540        // followed from the `execution_b` span.
541        let relationships = relationships.lock().unwrap();
542        assert_eq!(relationships.len(), 2);
543        assert_eq!(relationships[0].1, execution_a_span.id().unwrap());
544        assert_eq!(relationships[1].1, execution_b_span.id().unwrap());
545    }
546}