1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
//! # Slog Scopes for the Async World //! //! This crate provides a mechanism to use slog scopes with `Future`s. //! //! ## The Problem //! //! With synchronous code, slog-scope works as expected. But what about when //! dealing with `async`/`await`? //! //! This won't compile: //! //! ```compile_fail //! slog_scope::scope(&logger, || { //! some_operation().await // Error: can't use await outside of an async fn/block //! }) //! ``` //! //! This compiles, but doesn't do what you actually want: //! //! ```no_run //! # #![feature(async_await)] //! # async fn some_operation() {} //! # async { //! # use slog::o; //! let logger = slog_scope::logger().new(o!("name" => "sub logger")); //! //! let fut = slog_scope::scope(&logger, async || { // <- scope start //! some_operation().await //! }); // <- scope end //! //! fut.await // Scope not active here while the future is actually running //! # }; //! ``` //! //! ## The Solution //! //! Rather than using a closure to represent a slog scope, the logger must //! instead be tied to the future itself, and its `poll` method wrapped in //! a scope. The `SlogScope` type provides a `Future` wrapper that does exactly //! that. //! //! ### Usage //! //! Using the wrapper directly: //! //! ```rust,norun //! # #![feature(async_await)] //! # async fn some_operation() {} //! # async { //! # use slog::o; //! use slog_scope_futures::SlogScope; //! //! let logger = slog_scope::logger().new(o!("name" => "sub logger")); //! //! SlogScope::new(logger, some_operation()).await //! # }; //! ``` //! //! Using the convenience trait: //! //! ```rust,norun //! # #![feature(async_await)] //! # async fn some_operation() {} //! # async { //! # use slog::o; //! use slog_scope_futures::FutureExt; //! //! let logger = slog_scope::logger().new(o!("name" => "sub logger")); //! //! some_operation().with_logger(logger).await //! # }; //! ``` //! //! ### Borrowed vs Owned Loggers //! //! Often, you need a `Future` to be `'static` so that it can be spawned into //! an executor. Other times, though, you can get away with borrowing the //! logger. This way, it can be re-used without additional cloning of the //! handle. //! //! Because the `SlogScope` wrapper takes any `L: Borrow<Logger>`, you can //! create it with either an owned *or* a borrowed `Logger`. //! //! ```rust,norun //! # #![feature(async_await)] //! # async fn some_operation() {} //! # async fn some_other_operation() {} //! # async { //! # use slog::o; //! # use core::future::Future; //! use slog_scope_futures::FutureExt; //! //! let logger = slog_scope::logger().new(o!("name" => "sub logger")); //! //! some_operation().with_logger(&logger).await; // <- borrowed logger //! let fut = some_other_operation().with_logger(logger); // <- owned logger //! //! async fn assert_static<F: Future + 'static>(f: F) -> F::Output { f.await } //! //! assert_static(fut).await //! # }; //! ``` //! #![warn(missing_docs)] use std::borrow::Borrow; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use slog::Logger; /// A `Future` wrapped in a slog scope. pub struct SlogScope<L, F> { logger: L, inner: F, } impl<L, F> SlogScope<L, F> where F: Future, L: Borrow<Logger>, { /// Wrap a `Future` in a slog scope. pub fn new(logger: L, inner: F) -> Self { SlogScope { logger, inner } } } impl<L, F> Future for SlogScope<L, F> where F: Future, L: Borrow<Logger>, { type Output = F::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { // Safety: We're not moving any of this, the inner future, or the logger. let this = unsafe { self.get_unchecked_mut() }; let inner = unsafe { Pin::new_unchecked(&mut this.inner) }; let logger = &this.logger; slog_scope::scope(logger.borrow(), || inner.poll(cx)) } } /// Convenience trait for wrapping a `Future` in a slog scope via method chaining. /// /// Automatically implemented for all `Future`s. pub trait FutureExt: Future + Sized { /// Wrap `self` in a slog scope fn with_logger<L>(self, logger: L) -> SlogScope<L, Self> where L: Borrow<Logger>, { SlogScope::new(logger, self) } } impl<F> FutureExt for F where F: Future {}