pub struct ContextSnapshot { /* private fields */ }std only.Expand description
An owned, immutable copy of a set of context fields.
Send + Sync and cheap to clone, so it can be moved into threads,
tasks, and closures. Install it on the current thread with
ContextSnapshot::attach, ContextSnapshot::scope or
ContextSnapshot::wrap. An attached snapshot behaves like a
group of with_field entries: fields pushed later shadow it, and
it shadows fields pushed earlier.
Implementations§
Source§impl ContextSnapshot
impl ContextSnapshot
Sourcepub fn from_fields(fields: &[Field<'_>]) -> Self
pub fn from_fields(fields: &[Field<'_>]) -> Self
Build a snapshot from explicit fields, without touching the thread’s context. Later fields shadow earlier ones with the same key.
§Examples
use log_io::context::ContextSnapshot;
use log_io::Field;
let snap = ContextSnapshot::from_fields(&[Field::new("tenant", "acme")]);
assert_eq!(snap.len(), 1);Sourcepub fn iter(&self) -> impl Iterator<Item = Field<'_>>
pub fn iter(&self) -> impl Iterator<Item = Field<'_>>
Iterate over the snapshot’s fields in insertion order.
Sourcepub fn attach(&self) -> ContextGuard
pub fn attach(&self) -> ContextGuard
Install the snapshot on the current thread until the returned guard is dropped. Does not allocate.
Sourcepub fn scope<R>(&self, f: impl FnOnce() -> R) -> R
pub fn scope<R>(&self, f: impl FnOnce() -> R) -> R
Run f with the snapshot installed on the current thread.
Sourcepub fn wrap<F: Future>(&self, future: F) -> WithContext<F> ⓘ
pub fn wrap<F: Future>(&self, future: F) -> WithContext<F> ⓘ
Wrap a future so the snapshot is installed around every poll.
Works with any executor, including work-stealing ones: whichever thread polls the future sees the snapshot for exactly the duration of the poll.
Context the future itself pushes and holds across an .await
(a ContextGuard alive in the future’s state) is taken off the
thread when the poll returns Pending and put back at the start
of the next poll, so other futures polled on the same thread do
not see it. Such a future is !Send, so it always resumes on the
thread that created the guard.
The future is boxed once (one allocation per wrapped future) so
the wrapper can be Unpin without unsafe code; holding context
across an .await allocates once more, on first use.
§Examples
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, Waker};
use log_io::context::{self, ContextSnapshot};
/// Returns `Pending` once, like an `.await` on I/O.
struct YieldOnce(bool);
impl Future for YieldOnce {
type Output = ();
fn poll(mut self: std::pin::Pin<&mut Self>, _: &mut Context<'_>) -> Poll<()> {
if self.0 { Poll::Ready(()) } else { self.0 = true; Poll::Pending }
}
}
let mut task = pin!(ContextSnapshot::default().wrap(async {
let _tenant = context::with_field("tenant", "a");
YieldOnce(false).await;
assert_eq!(context::len(), 1); // back while this future runs
}));
let mut cx = Context::from_waker(Waker::noop());
assert!(task.as_mut().poll(&mut cx).is_pending());
assert_eq!(context::len(), 0); // not visible to anything else
assert!(task.as_mut().poll(&mut cx).is_ready());