Skip to content

Commit d3fa53f

Browse files
committed
tracing: add missing Future impl for WithDispatch (#1602)
The version of `WithSubscriber` in `tracing::instrument` (rather than in `tracing-futures`) is currently...useless, since there is no `Future` impl for the `WithDispatch` type. This means that calling `with_collector` on a `Future` returns a value that _isn't_ a `Future`. Additionally, the `WithSubscriber` trait isn't actually implemented for anything (although this was fixed on v0.1.x). This branch adds the missing implementations. I also improved the docs a bit. Note that the `Future` impl requires the "std" feature flag, but the `WithSubscriber` trait and `WithDispatch` type do not. This is because requiring the feature flag for these definitions would *technically* be a breaking change, since they were previously published without the feature flag and could e.g. be imported...even though they were totally useless. Sigh. Signed-off-by: Eliza Weisman <eliza@buoyant.io> ; Conflicts: ; tracing/src/instrument.rs
1 parent 37c4b5c commit d3fa53f

File tree

2 files changed

+170
-35
lines changed

2 files changed

+170
-35
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ jobs:
239239
run: cargo test --no-default-features
240240
working-directory: tracing
241241
- name: "Test tracing no-std support"
242-
run: cargo test --no-default-features
242+
run: cargo test --lib --tests --no-default-features
243243
working-directory: tracing
244244
# this skips running doctests under the `--no-default-features` flag,
245245
# as rustdoc isn't aware of cargo's feature flags.

tracing/src/instrument.rs

Lines changed: 169 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
use crate::stdlib::pin::Pin;
22
use crate::stdlib::task::{Context, Poll};
33
use crate::stdlib::{future::Future, marker::Sized};
4-
use crate::{dispatcher, span::Span, Dispatch};
4+
use crate::{
5+
dispatcher::{self, Dispatch},
6+
span::Span,
7+
};
58
use pin_project_lite::pin_project;
69

7-
/// Attaches spans to a `std::future::Future`.
10+
/// Attaches spans to a [`std::future::Future`].
811
///
912
/// Extension trait allowing futures to be
1013
/// instrumented with a `tracing` [span].
1114
///
12-
/// [span]: ../struct.Span.html
15+
/// [span]: super::Span
1316
pub trait Instrument: Sized {
14-
/// Instruments this type with the provided `Span`, returning an
17+
/// Instruments this type with the provided [`Span`], returning an
1518
/// `Instrumented` wrapper.
1619
///
17-
/// The attached `Span` will be [entered] every time the instrumented `Future` is polled.
20+
/// The attached [`Span`] will be [entered] every time the instrumented
21+
/// [`Future`] is polled.
1822
///
1923
/// # Examples
2024
///
@@ -38,7 +42,7 @@ pub trait Instrument: Sized {
3842
/// `instrument` to ensure that the [current span] is attached to the
3943
/// future if the span passed to `instrument` is [disabled]:
4044
///
41-
/// ```W
45+
/// ```
4246
/// use tracing::Instrument;
4347
/// # mod tokio {
4448
/// # pub(super) fn spawn(_: impl std::future::Future) {}
@@ -74,17 +78,16 @@ pub trait Instrument: Sized {
7478
/// [`Span::or_current`]: super::Span::or_current()
7579
/// [current span]: super::Span::current()
7680
/// [disabled]: super::Span::is_disabled()
81+
/// [`Future`]: std::future::Future
7782
fn instrument(self, span: Span) -> Instrumented<Self> {
7883
Instrumented { inner: self, span }
7984
}
8085

81-
/// Instruments this type with the [current] `Span`, returning an
86+
/// Instruments this type with the [current] [`Span`], returning an
8287
/// `Instrumented` wrapper.
8388
///
84-
/// If the instrumented type is a future, stream, or sink, the attached `Span`
85-
/// will be [entered] every time it is polled. If the instrumented type
86-
/// is a future executor, every future spawned on that executor will be
87-
/// instrumented by the attached `Span`.
89+
/// The attached [`Span`] will be [entered] every time the instrumented
90+
/// [`Future`] is polled.
8891
///
8992
/// This can be used to propagate the current span when spawning a new future.
9093
///
@@ -93,6 +96,9 @@ pub trait Instrument: Sized {
9396
/// ```rust
9497
/// use tracing::Instrument;
9598
///
99+
/// # mod tokio {
100+
/// # pub(super) fn spawn(_: impl std::future::Future) {}
101+
/// # }
96102
/// # async fn doc() {
97103
/// let span = tracing::info_span!("my_span");
98104
/// let _enter = span.enter();
@@ -107,8 +113,10 @@ pub trait Instrument: Sized {
107113
/// # }
108114
/// ```
109115
///
110-
/// [current]: ../struct.Span.html#method.current
111-
/// [entered]: ../struct.Span.html#method.enter
116+
/// [current]: super::Span::current()
117+
/// [entered]: super::Span::enter()
118+
/// [`Span`]: crate::Span
119+
/// [`Future`]: std::future::Future
112120
#[inline]
113121
fn in_current_span(self) -> Instrumented<Self> {
114122
self.instrument(Span::current())
@@ -117,63 +125,133 @@ pub trait Instrument: Sized {
117125

118126
/// Extension trait allowing futures to be instrumented with
119127
/// a `tracing` [`Subscriber`].
120-
///
121-
/// [`Subscriber`]: ../trait.Subscriber.html
128+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
122129
pub trait WithSubscriber: Sized {
123130
/// Attaches the provided [`Subscriber`] to this type, returning a
124-
/// `WithDispatch` wrapper.
131+
/// [`WithDispatch`] wrapper.
132+
///
133+
/// The attached [`Subscriber`] will be set as the [default] when the returned
134+
/// [`Future`] is polled.
135+
///
136+
/// # Examples
137+
///
138+
/// ```
139+
/// # use tracing::subscriber::NoSubscriber as MySubscriber;
140+
/// # use tracing::subscriber::NoSubscriber as MyOtherSubscriber;
141+
/// # async fn docs() {
142+
/// use tracing::instrument::WithSubscriber;
125143
///
126-
/// The attached subscriber will be set as the [default] when the returned `Future` is polled.
144+
/// // Set the default `Subscriber`
145+
/// let _default = tracing::subscriber::set_default(MySubscriber::default());
127146
///
128-
/// [`Subscriber`]: ../trait.Subscriber.html
129-
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
147+
/// tracing::info!("this event will be recorded by the default `Subscriber`");
148+
///
149+
/// // Create a different `Subscriber` and attach it to a future.
150+
/// let other_subscriber = MyOtherSubscriber::default();
151+
/// let future = async {
152+
/// tracing::info!("this event will be recorded by the other `Subscriber`");
153+
/// // ...
154+
/// };
155+
///
156+
/// future
157+
/// // Attach the other `Subscriber` to the future before awaiting it
158+
/// .with_subscriber(other_subscriber)
159+
/// .await;
160+
///
161+
/// // Once the future has completed, we return to the default `Subscriber`.
162+
/// tracing::info!("this event will be recorded by the default `Subscriber`");
163+
/// # }
164+
/// ```
165+
///
166+
/// [`Subscriber`]: super::Subscriber
167+
/// [default]: crate::dispatcher#setting-the-default-subscriber
168+
/// [`Future`]: std::future::Future
130169
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
131170
where
132171
S: Into<Dispatch>,
133172
{
134173
WithDispatch {
135174
inner: self,
136-
dispatch: subscriber.into(),
175+
dispatcher: subscriber.into(),
137176
}
138177
}
139178

140179
/// Attaches the current [default] [`Subscriber`] to this type, returning a
141-
/// `WithDispatch` wrapper.
180+
/// [`WithDispatch`] wrapper.
142181
///
143-
/// When the wrapped type is a future, stream, or sink, the attached
144-
/// subscriber will be set as the [default] while it is being polled.
145-
/// When the wrapped type is an executor, the subscriber will be set as the
146-
/// default for any futures spawned on that executor.
182+
/// The attached `Subscriber` will be set as the [default] when the returned
183+
/// [`Future`] is polled.
147184
///
148185
/// This can be used to propagate the current dispatcher context when
149-
/// spawning a new future.
186+
/// spawning a new future that may run on a different thread.
150187
///
151-
/// [`Subscriber`]: ../trait.Subscriber.html
152-
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
188+
/// # Examples
189+
///
190+
/// ```
191+
/// # mod tokio {
192+
/// # pub(super) fn spawn(_: impl std::future::Future) {}
193+
/// # }
194+
/// # use tracing::subscriber::NoSubscriber as MySubscriber;
195+
/// # async fn docs() {
196+
/// use tracing::instrument::WithSubscriber;
197+
///
198+
/// // Using `set_default` (rather than `set_global_default`) sets the
199+
/// // default `Subscriber` for *this* thread only.
200+
/// let _default = tracing::subscriber::set_default(MySubscriber::default());
201+
///
202+
/// let future = async {
203+
/// // ...
204+
/// };
205+
///
206+
/// // If a multi-threaded async runtime is in use, this spawned task may
207+
/// // run on a different thread, in a different default `Subscriber`'s context.
208+
/// tokio::spawn(future);
209+
///
210+
/// // However, calling `with_current_subscriber` on the future before
211+
/// // spawning it, ensures that the current thread's default `Subscriber` is
212+
/// // propagated to the spawned task, regardless of where it executes:
213+
/// # let future = async { };
214+
/// tokio::spawn(future.with_current_subscriber());
215+
/// # }
216+
/// ```
217+
/// [`Subscriber`]: super::Subscriber
218+
/// [default]: crate::dispatcher#setting-the-default-subscriber
219+
/// [`Future`]: std::future::Future
153220
#[inline]
154221
fn with_current_subscriber(self) -> WithDispatch<Self> {
155222
WithDispatch {
156223
inner: self,
157-
dispatch: dispatcher::get_default(|default| default.clone()),
224+
dispatcher: crate::dispatcher::get_default(|default| default.clone()),
158225
}
159226
}
160227
}
161228

162-
impl<T: Sized> WithSubscriber for T {}
163-
164229
pin_project! {
165-
/// A future that has been instrumented with a `tracing` subscriber.
230+
/// A [`Future`] that has been instrumented with a `tracing` [`Subscriber`].
231+
///
232+
/// This type is returned by the [`WithSubscriber`] extension trait. See that
233+
/// trait's documentation for details.
234+
///
235+
/// [`Future`]: std::future::Future
236+
/// [`Subscriber`]: crate::Subscriber
166237
#[derive(Clone, Debug)]
167238
#[must_use = "futures do nothing unless you `.await` or poll them"]
239+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
168240
pub struct WithDispatch<T> {
169241
#[pin]
170242
inner: T,
171-
dispatch: Dispatch,
243+
dispatcher: Dispatch,
172244
}
173245
}
174246

175247
pin_project! {
176-
/// A future that has been instrumented with a `tracing` span.
248+
/// A [`Future`] that has been instrumented with a `tracing` [`Span`].
249+
///
250+
/// This type is returned by the [`Instrument`] extension trait. See that
251+
/// trait's documentation for details.
252+
///
253+
/// [`Future`]: std::future::Future
254+
/// [`Span`]: crate::Span
177255
#[derive(Debug, Clone)]
178256
#[must_use = "futures do nothing unless you `.await` or poll them"]
179257
pub struct Instrumented<T> {
@@ -183,6 +261,8 @@ pin_project! {
183261
}
184262
}
185263

264+
// === impl Instrumented ===
265+
186266
impl<T: Future> Future for Instrumented<T> {
187267
type Output = T::Output;
188268

@@ -233,3 +313,58 @@ impl<T> Instrumented<T> {
233313
self.inner
234314
}
235315
}
316+
317+
// === impl WithDispatch ===
318+
319+
#[cfg(feature = "std")]
320+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
321+
impl<T: Future> Future for WithDispatch<T> {
322+
type Output = T::Output;
323+
324+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
325+
let this = self.project();
326+
let dispatcher = this.dispatcher;
327+
let future = this.inner;
328+
let _default = dispatcher::set_default(dispatcher);
329+
future.poll(cx)
330+
}
331+
}
332+
333+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
334+
impl<T: Sized> WithSubscriber for T {}
335+
336+
#[cfg(feature = "std")]
337+
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
338+
impl<T> WithDispatch<T> {
339+
/// Borrows the [`Dispatch`] that is entered when this type is polled.
340+
pub fn dispatcher(&self) -> &Dispatch {
341+
&self.dispatcher
342+
}
343+
344+
/// Borrows the wrapped type.
345+
pub fn inner(&self) -> &T {
346+
&self.inner
347+
}
348+
349+
/// Mutably borrows the wrapped type.
350+
pub fn inner_mut(&mut self) -> &mut T {
351+
&mut self.inner
352+
}
353+
354+
/// Get a pinned reference to the wrapped type.
355+
pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> {
356+
self.project_ref().inner
357+
}
358+
359+
/// Get a pinned mutable reference to the wrapped type.
360+
pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
361+
self.project().inner
362+
}
363+
364+
/// Consumes the `Instrumented`, returning the wrapped type.
365+
///
366+
/// Note that this drops the span.
367+
pub fn into_inner(self) -> T {
368+
self.inner
369+
}
370+
}

0 commit comments

Comments
 (0)