use std::future::Future;
use tracing::Instrument;
pub async fn in_span<F: Future>(span: tracing::Span, fut: F) -> F::Output {
fut.instrument(span).await
}
pub struct Spanned {
span: tracing::Span,
}
impl Spanned {
pub fn new(span: tracing::Span) -> Self {
Self { span }
}
pub async fn run<F: Future>(self, fut: F) -> F::Output {
fut.instrument(self.span).await
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::registry;
#[derive(Clone, Default)]
struct RecordingLayer {
entered: Arc<Mutex<Vec<String>>>,
}
impl<S> tracing_subscriber::Layer<S> for RecordingLayer
where
S: tracing::Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
{
fn on_enter(&self, id: &tracing::span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
if let Some(span) = ctx.span(id) {
self.entered.lock().unwrap().push(span.name().to_string());
}
}
}
#[tokio::test]
async fn in_span_enters_span_around_await_points() {
let layer = RecordingLayer::default();
let entered = layer.entered.clone();
let subscriber = registry().with(layer);
let _guard = tracing::subscriber::set_default(subscriber);
let span = tracing::info_span!("sealwiz.wrap");
let out = in_span(span, async {
tokio::task::yield_now().await;
"wrapped"
})
.await;
assert_eq!(out, "wrapped");
assert!(entered.lock().unwrap().iter().any(|n| n == "sealwiz.wrap"));
}
#[tokio::test]
async fn spanned_run_survives_yield_across_await() {
let layer = RecordingLayer::default();
let entered = layer.entered.clone();
let subscriber = registry().with(layer);
let _guard = tracing::subscriber::set_default(subscriber);
let span = tracing::info_span!("sealwiz.unwrap");
let out = Spanned::new(span)
.run(async {
tokio::task::yield_now().await;
tokio::task::yield_now().await;
42
})
.await;
assert_eq!(out, 42);
assert!(
entered
.lock()
.unwrap()
.iter()
.any(|n| n == "sealwiz.unwrap")
);
}
}