1//! Futures compatibility for [`tracing`].2//!3//! # Overview4//!5//! [`tracing`] is a framework for instrumenting Rust programs to collect6//! structured, event-based diagnostic information. This crate provides utilities7//! for using `tracing` to instrument asynchronous code written using futures and8//! async/await.9//!10//! The crate provides the following traits:11//!12//! * [`Instrument`] allows a `tracing` [span] to be attached to a future, sink,13//! stream, or executor.14//!15//! * [`WithSubscriber`] allows a `tracing` [`Subscriber`] to be attached to a16//! future, sink, stream, or executor.17//!18//! *Compiler support: [requires `rustc` 1.42+][msrv]*19//!20//! [msrv]: #supported-rust-versions21//!22//! # Feature flags23//!24//! This crate provides a number of feature flags that enable compatibility25//! features with other crates in the asynchronous ecosystem:26//!27//! - `tokio`: Enables compatibility with the `tokio` crate, including28//! [`Instrument`] and [`WithSubscriber`] implementations for29//! `tokio::executor::Executor`, `tokio::runtime::Runtime`, and30//! `tokio::runtime::current_thread`. Enabled by default.31//! - `tokio-executor`: Enables compatibility with the `tokio-executor`32//! crate, including [`Instrument`] and [`WithSubscriber`]33//! implementations for types implementing `tokio_executor::Executor`.34//! This is intended primarily for use in crates which depend on35//! `tokio-executor` rather than `tokio`; in general the `tokio` feature36//! should be used instead.37//! - `std-future`: Enables compatibility with `std::future::Future`.38//! - `futures-01`: Enables compatibility with version 0.1.x of the [`futures`]39//! crate.40//! - `futures-03`: Enables compatibility with version 0.3.x of the `futures`41//! crate's `Spawn` and `LocalSpawn` traits.42//! - `tokio-alpha`: Enables compatibility with `tokio` 0.2's alpha releases,43//! including the `tokio` 0.2 `Executor` and `TypedExecutor` traits.44//! - `std`: Depend on the Rust standard library.45//!46//! `no_std` users may disable this feature with `default-features = false`:47//!48//! ```toml49//! [dependencies]50//! tracing-futures = { version = "0.2.5", default-features = false }51//! ```52//!53//! The `tokio`, `std-future` and `std` features are enabled by default.54//!55//! [`tracing`]: https://crates.io/crates/tracing56//! [span]: https://docs.rs/tracing/latest/tracing/span/index.html57//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/index.html58//! [`Instrument`]: trait.Instrument.html59//! [`WithSubscriber`]: trait.WithSubscriber.html60//! [`futures`]: https://crates.io/crates/futures61//!62//! ## Supported Rust Versions63//!64//! Tracing is built against the latest stable release. The minimum supported65//! version is 1.42. The current Tracing version is not guaranteed to build on66//! Rust versions earlier than the minimum supported version.67//!68//! Tracing follows the same compiler support policies as the rest of the Tokio69//! project. The current stable Rust compiler and the three most recent minor70//! versions before it will always be supported. For example, if the current71//! stable compiler version is 1.45, the minimum supported version will not be72//! increased past 1.42, three minor versions prior. Increasing the minimum73//! supported compiler version is not considered a semver breaking change as74//! long as doing so complies with this policy.75//!76#![doc(html_root_url ="https://docs.rs/tracing-futures/0.2.5")]77#![doc(78 html_logo_url ="https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",79 issue_tracker_base_url ="https://github.com/tokio-rs/tracing/issues/"80)]81#![warn(82 missing_debug_implementations,83 missing_docs,84 rust_2018_idioms,85 unreachable_pub,86 bad_style,87 const_err,88 dead_code,89 improper_ctypes,90 non_shorthand_field_patterns,91 no_mangle_generic_items,92 overflowing_literals,93 path_statements,94 patterns_in_fns_without_body,95 private_in_public,96 unconditional_recursion,97 unused,98 unused_allocation,99 unused_comparisons,100 unused_parens,101 while_true102)]103#![cfg_attr(not(feature ="std"), no_std)]104#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))]105#[cfg(feature ="std-future")]106usepin_project::pin_project;107108pub(crate)modstdlib;109110#[cfg(feature ="std-future")]111usecrate::stdlib::{pin::Pin, task::Context};112113#[cfg(feature ="std")]114usetracing::{dispatcher, Dispatch};115116usetracing::Span;117118/// Implementations for `Instrument`ed future executors.119pub modexecutor;120121/// Extension trait allowing futures, streams, sinks, and executors to be122/// instrumented with a `tracing` [span].123///124/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html125pub traitInstrument: Sized {126/// Instruments this type with the provided `Span`, returning an127 /// `Instrumented` wrapper.128 ///129 /// If the instrumented type is a future, stream, or sink, the attached `Span`130 /// will be [entered] every time it is polled. If the instrumented type131 /// is a future executor, every future spawned on that executor will be132 /// instrumented by the attached `Span`.133 ///134 /// # Examples135 ///136 /// Instrumenting a future:137 ///138// TODO: ignored until async-await is stable...139/// ```rust,ignore140 /// use tracing_futures::Instrument;141 ///142 /// # async fn doc() {143 /// let my_future = async {144 /// // ...145 /// };146 ///147 /// my_future148 /// .instrument(tracing::info_span!("my_future"))149 /// .await150 /// # }151 /// ```152 ///153 /// [entered]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html#method.enter154fninstrument(self, span: Span) -> Instrumented<Self> {155 Instrumented { inner:self, span }156 }157158/// Instruments this type with the [current] `Span`, returning an159 /// `Instrumented` wrapper.160 ///161 /// If the instrumented type is a future, stream, or sink, the attached `Span`162 /// will be [entered] every time it is polled. If the instrumented type163 /// is a future executor, every future spawned on that executor will be164 /// instrumented by the attached `Span`.165 ///166 /// This can be used to propagate the current span when spawning a new future.167 ///168 /// # Examples169 ///170// TODO: ignored until async-await is stable...171/// ```rust,ignore172 /// use tracing_futures::Instrument;173 ///174 /// # async fn doc() {175 /// let span = tracing::info_span!("my_span");176 /// let _enter = span.enter();177 ///178 /// // ...179 ///180 /// let future = async {181 /// tracing::debug!("this event will occur inside `my_span`");182 /// // ...183 /// };184 /// tokio::spawn(future.in_current_span());185 /// # }186 /// ```187 ///188 /// [current]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html#method.current189 /// [entered]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html#method.enter190#[inline]191fnin_current_span(self) -> Instrumented<Self> {192self.instrument(Span::current())193 }194}195196/// Extension trait allowing futures, streams, and sinks to be instrumented with197/// a `tracing` [`Subscriber`].198///199/// [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/trait.Subscriber.html200#[cfg(feature ="std")]201#[cfg_attr(docsrs, doc(cfg(feature ="std")))]202pub traitWithSubscriber: Sized {203/// Attaches the provided [`Subscriber`] to this type, returning a204 /// `WithDispatch` wrapper.205 ///206 /// When the wrapped type is a future, stream, or sink, the attached207 /// subscriber will be set as the [default] while it is being polled.208 /// When the wrapped type is an executor, the subscriber will be set as the209 /// default for any futures spawned on that executor.210 ///211 /// [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/trait.Subscriber.html212 /// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber213fnwith_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>214where215S: Into<Dispatch>,216 {217 WithDispatch {218 inner:self,219 dispatch: subscriber.into(),220 }221 }222223/// Attaches the current [default] [`Subscriber`] to this type, returning a224 /// `WithDispatch` wrapper.225 ///226 /// When the wrapped type is a future, stream, or sink, the attached227 /// subscriber will be set as the [default] while it is being polled.228 /// When the wrapped type is an executor, the subscriber will be set as the229 /// default for any futures spawned on that executor.230 ///231 /// This can be used to propagate the current dispatcher context when232 /// spawning a new future.233 ///234 /// [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/trait.Subscriber.html235 /// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber236#[inline]237fnwith_current_subscriber(self) -> WithDispatch<Self> {238 WithDispatch {239 inner:self,240 dispatch: dispatcher::get_default(|default| default.clone()),241 }242 }243}244245/// A future, stream, sink, or executor that has been instrumented with a `tracing` span.246#[cfg_attr(feature ="std-future", pin_project)]247#[derive(Debug, Clone)]248pub structInstrumented<T> {249#[cfg(feature ="std-future")]250 #[pin]251inner: T,252#[cfg(not(feature ="std-future"))]253inner: T,254 span: Span,255}256257/// A future, stream, sink, or executor that has been instrumented with a258/// `tracing` subscriber.259#[cfg(feature ="std")]260#[cfg_attr(docsrs, doc(cfg(feature ="std")))]261#[cfg_attr(feature ="std-future", pin_project)]262#[derive(Clone, Debug)]263pub structWithDispatch<T> {264// cfg_attr doesn't work inside structs, apparently...265#[cfg(feature ="std-future")]266 #[pin]267inner: T,268#[cfg(not(feature ="std-future"))]269inner: T,270 dispatch: Dispatch,271}272273impl<T: Sized> InstrumentforT {}274275#[cfg(feature ="std-future")]276#[cfg_attr(docsrs, doc(cfg(feature ="std-future")))]277impl<T:crate::stdlib::future::Future>crate::stdlib::future::FutureforInstrumented<T> {278typeOutput = T::Output;279280fnpoll(self: Pin<&mutSelf>, cx:&mutContext<'_>) ->crate::stdlib::task::Poll<Self::Output> {281letthis =self.project();282let_enter = this.span.enter();283 this.inner.poll(cx)284 }285}286287#[cfg(feature ="futures-01")]288#[cfg_attr(docsrs, doc(cfg(feature ="futures-01")))]289impl<T: futures_01::Future> futures_01::FutureforInstrumented<T> {290typeItem = T::Item;291typeError = T::Error;292293fnpoll(&mutself) -> futures_01::Poll<Self::Item,Self::Error> {294let_enter =self.span.enter();295self.inner.poll()296 }297}298299#[cfg(feature ="futures-01")]300#[cfg_attr(docsrs, doc(cfg(feature ="futures-01")))]301impl<T: futures_01::Stream> futures_01::StreamforInstrumented<T> {302typeItem = T::Item;303typeError = T::Error;304305fnpoll(&mutself) -> futures_01::Poll<Option<Self::Item>,Self::Error> {306let_enter =self.span.enter();307self.inner.poll()308 }309}310311#[cfg(feature ="futures-01")]312#[cfg_attr(docsrs, doc(cfg(feature ="futures-01")))]313impl<T: futures_01::Sink> futures_01::SinkforInstrumented<T> {314typeSinkItem = T::SinkItem;315typeSinkError = T::SinkError;316317fnstart_send(318&mutself,319 item:Self::SinkItem,320 ) -> futures_01::StartSend<Self::SinkItem,Self::SinkError> {321let_enter =self.span.enter();322self.inner.start_send(item)323 }324325fnpoll_complete(&mutself) -> futures_01::Poll<(),Self::SinkError> {326let_enter =self.span.enter();327self.inner.poll_complete()328 }329}330331#[cfg(all(feature ="futures-03", feature ="std-future"))]332#[cfg_attr(docsrs, doc(cfg(all(feature ="futures-03", feature ="std-future"))))]333impl<T: futures::Stream> futures::StreamforInstrumented<T> {334typeItem = T::Item;335336fnpoll_next(337self: Pin<&mutSelf>,338 cx:&mutContext<'_>,339 ) -> futures::task::Poll<Option<Self::Item>> {340letthis =self.project();341let_enter = this.span.enter();342 T::poll_next(this.inner, cx)343 }344}345346#[cfg(all(feature ="futures-03", feature ="std-future"))]347#[cfg_attr(docsrs, doc(cfg(all(feature ="futures-03", feature ="std-future"))))]348impl<I, T: futures::Sink<I>> futures::Sink<I>forInstrumented<T>349where350T: futures::Sink<I>,351{352typeError = T::Error;353354fnpoll_ready(355self: Pin<&mutSelf>,356 cx:&mutContext<'_>,357 ) -> futures::task::Poll<Result<(),Self::Error>> {358letthis =self.project();359let_enter = this.span.enter();360 T::poll_ready(this.inner, cx)361 }362363fnstart_send(self: Pin<&mutSelf>, item: I) ->Result<(),Self::Error> {364letthis =self.project();365let_enter = this.span.enter();366 T::start_send(this.inner, item)367 }368369fnpoll_flush(370self: Pin<&mutSelf>,371 cx:&mutContext<'_>,372 ) -> futures::task::Poll<Result<(),Self::Error>> {373letthis =self.project();374let_enter = this.span.enter();375 T::poll_flush(this.inner, cx)376 }377378fnpoll_close(379self: Pin<&mutSelf>,380 cx:&mutContext<'_>,381 ) -> futures::task::Poll<Result<(),Self::Error>> {382letthis =self.project();383let_enter = this.span.enter();384 T::poll_close(this.inner, cx)385 }386}387388impl<T> Instrumented<T> {389/// Borrows the `Span` that this type is instrumented by.390pub fnspan(&self) ->&Span {391&self.span392 }393394/// Mutably borrows the `Span` that this type is instrumented by.395pub fnspan_mut(&mutself) ->&mutSpan {396&mutself.span397 }398399/// Borrows the wrapped type.400pub fninner(&self) ->&T {401&self.inner402 }403404/// Mutably borrows the wrapped type.405pub fninner_mut(&mutself) ->&mutT {406&mutself.inner407 }408409/// Get a pinned reference to the wrapped type.410#[cfg(feature ="std-future")]411 #[cfg_attr(docsrs, doc(cfg(feature ="std-future")))]412pub fninner_pin_ref(self: Pin<&Self>) -> Pin<&T> {413self.project_ref().inner414 }415416/// Get a pinned mutable reference to the wrapped type.417#[cfg(feature ="std-future")]418 #[cfg_attr(docsrs, doc(cfg(feature ="std-future")))]419pub fninner_pin_mut(self: Pin<&mutSelf>) -> Pin<&mutT> {420self.project().inner421 }422423/// Consumes the `Instrumented`, returning the wrapped type.424 ///425 /// Note that this drops the span.426pub fninto_inner(self) -> T {427self.inner428 }429}430431#[cfg(feature ="std")]432impl<T: Sized> WithSubscriberforT {}433434#[cfg(all(feature ="futures-01", feature ="std"))]435#[cfg_attr(docsrs, doc(cfg(all(feature ="futures-01", feature ="std"))))]436impl<T: futures_01::Future> futures_01::FutureforWithDispatch<T> {437typeItem = T::Item;438typeError = T::Error;439440fnpoll(&mutself) -> futures_01::Poll<Self::Item,Self::Error> {441letinner =&mutself.inner;442 dispatcher::with_default(&self.dispatch, || inner.poll())443 }444}445446#[cfg(all(feature ="std-future", feature ="std"))]447#[cfg_attr(docsrs, doc(cfg(all(feature ="std-future", feature ="std"))))]448impl<T:crate::stdlib::future::Future>crate::stdlib::future::FutureforWithDispatch<T> {449typeOutput = T::Output;450451fnpoll(self: Pin<&mutSelf>, cx:&mutContext<'_>) ->crate::stdlib::task::Poll<Self::Output> {452letthis =self.project();453letdispatch = this.dispatch;454letfuture = this.inner;455 dispatcher::with_default(dispatch, || future.poll(cx))456 }457}458459#[cfg(feature ="std")]460impl<T> WithDispatch<T> {461/// Wrap a future, stream, sink or executor with the same subscriber as this WithDispatch.462pub fnwith_dispatch<U>(&self, inner: U) -> WithDispatch<U> {463 WithDispatch {464 dispatch:self.dispatch.clone(),465 inner,466 }467 }468469/// Borrows the `Dispatch` that this type is instrumented by.470pub fndispatch(&self) ->&Dispatch {471&self.dispatch472 }473474/// Get a pinned reference to the wrapped type.475#[cfg(feature ="std-future")]476 #[cfg_attr(docsrs, doc(cfg(feature ="std-future")))]477pub fninner_pin_ref(self: Pin<&Self>) -> Pin<&T> {478self.project_ref().inner479 }480481/// Get a pinned mutable reference to the wrapped type.482#[cfg(feature ="std-future")]483 #[cfg_attr(docsrs, doc(cfg(feature ="std-future")))]484pub fninner_pin_mut(self: Pin<&mutSelf>) -> Pin<&mutT> {485self.project().inner486 }487488/// Borrows the wrapped type.489pub fninner(&self) ->&T {490&self.inner491 }492493/// Mutably borrows the wrapped type.494pub fninner_mut(&mutself) ->&mutT {495&mutself.inner496 }497498/// Consumes the `WithDispatch`, returning the wrapped type.499pub fninto_inner(self) -> T {500self.inner501 }502}503504#[cfg(test)]505pub(crate)useself::supportastest_support;506// This has to have the same name as the module in `tracing`.507#[path ="../../tracing/tests/support/mod.rs"]508#[cfg(test)]509#[allow(unreachable_pub)]510pub(crate)modsupport;511512#[cfg(test)]513modtests {514use super::{test_support::*,*};515516#[cfg(feature ="futures-01")]517modfutures_01_tests {518usefutures_01::{future, stream, task, Async, Future, Stream};519usetracing::subscriber::with_default;520521use super::*;522523structPollN<T, E> {524 and_return:Option<Result<T, E>>,525 finish_at: usize,526 polls: usize,527 }528529implPollN<(), ()> {530fnnew_ok(finish_at: usize) ->Self{531Self{532 and_return:Some(Ok(())),533 finish_at,534 polls:0,535 }536 }537538fnnew_err(finish_at: usize) ->Self{539Self{540 and_return:Some(Err(())),541 finish_at,542 polls:0,543 }544 }545 }546547impl<T, E> futures_01::FutureforPollN<T, E> {548typeItem = T;549typeError = E;550fnpoll(&mutself) -> futures_01::Poll<Self::Item,Self::Error> {551self.polls +=1;552ifself.polls ==self.finish_at {553self.and_return554 .take()555 .expect("polled after ready")556 .map(Async::Ready)557 }else{558 task::current().notify();559Ok(Async::NotReady)560 }561 }562 }563564#[test]565fnfuture_enter_exit_is_reasonable() {566let(subscriber, handle) = subscriber::mock()567 .enter(span::mock().named("foo"))568 .exit(span::mock().named("foo"))569 .enter(span::mock().named("foo"))570 .exit(span::mock().named("foo"))571 .drop_span(span::mock().named("foo"))572 .done()573 .run_with_handle();574 with_default(subscriber, || {575 PollN::new_ok(2)576 .instrument(tracing::trace_span!("foo"))577 .wait()578 .unwrap();579 });580 handle.assert_finished();581 }582583#[test]584fnfuture_error_ends_span() {585let(subscriber, handle) = subscriber::mock()586 .enter(span::mock().named("foo"))587 .exit(span::mock().named("foo"))588 .enter(span::mock().named("foo"))589 .exit(span::mock().named("foo"))590 .drop_span(span::mock().named("foo"))591 .done()592 .run_with_handle();593 with_default(subscriber, || {594 PollN::new_err(2)595 .instrument(tracing::trace_span!("foo"))596 .wait()597 .unwrap_err();598 });599600 handle.assert_finished();601 }602603#[test]604fnstream_enter_exit_is_reasonable() {605let(subscriber, handle) = subscriber::mock()606 .enter(span::mock().named("foo"))607 .exit(span::mock().named("foo"))608 .enter(span::mock().named("foo"))609 .exit(span::mock().named("foo"))610 .enter(span::mock().named("foo"))611 .exit(span::mock().named("foo"))612 .enter(span::mock().named("foo"))613 .exit(span::mock().named("foo"))614 .drop_span(span::mock().named("foo"))615 .run_with_handle();616 with_default(subscriber, || {617 stream::iter_ok::<_, ()>(&[1,2,3])618 .instrument(tracing::trace_span!("foo"))619 .for_each(|_| future::ok(()))620 .wait()621 .unwrap();622 });623 handle.assert_finished();624 }625626#[test]627fnspan_follows_future_onto_threadpool() {628let(subscriber, handle) = subscriber::mock()629 .enter(span::mock().named("a"))630 .enter(span::mock().named("b"))631 .exit(span::mock().named("b"))632 .enter(span::mock().named("b"))633 .exit(span::mock().named("b"))634 .drop_span(span::mock().named("b"))635 .exit(span::mock().named("a"))636 .drop_span(span::mock().named("a"))637 .done()638 .run_with_handle();639letmutruntime = tokio::runtime::Runtime::new().unwrap();640 with_default(subscriber, || {641tracing::trace_span!("a").in_scope(|| {642letfuture = PollN::new_ok(2)643 .instrument(tracing::trace_span!("b"))644 .map(|_| {645tracing::trace_span!("c").in_scope(|| {646// "c" happens _outside_ of the instrumented future's647 // span, so we don't expect it.648})649 });650 runtime.block_on(Box::new(future)).unwrap();651 })652 });653 handle.assert_finished();654 }655 }656657#[cfg(all(feature ="futures-03", feature ="std-future"))]658modfutures_03_tests {659usefutures::{future, sink, stream, FutureExt, SinkExt, StreamExt};660usetracing::subscriber::with_default;661662use super::*;663664#[test]665fnstream_enter_exit_is_reasonable() {666let(subscriber, handle) = subscriber::mock()667 .enter(span::mock().named("foo"))668 .exit(span::mock().named("foo"))669 .enter(span::mock().named("foo"))670 .exit(span::mock().named("foo"))671 .enter(span::mock().named("foo"))672 .exit(span::mock().named("foo"))673 .enter(span::mock().named("foo"))674 .exit(span::mock().named("foo"))675 .drop_span(span::mock().named("foo"))676 .run_with_handle();677 with_default(subscriber, || {678 Instrument::instrument(stream::iter(&[1,2,3]),tracing::trace_span!("foo"))679 .for_each(|_| future::ready(()))680 .now_or_never()681 .unwrap();682 });683 handle.assert_finished();684 }685686#[test]687fnsink_enter_exit_is_reasonable() {688let(subscriber, handle) = subscriber::mock()689 .enter(span::mock().named("foo"))690 .exit(span::mock().named("foo"))691 .enter(span::mock().named("foo"))692 .exit(span::mock().named("foo"))693 .enter(span::mock().named("foo"))694 .exit(span::mock().named("foo"))695 .drop_span(span::mock().named("foo"))696 .run_with_handle();697 with_default(subscriber, || {698 Instrument::instrument(sink::drain(),tracing::trace_span!("foo"))699 .send(1u8)700 .now_or_never()701 .unwrap()702 .unwrap()703 });704 handle.assert_finished();705 }706 }707}