use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use tracing::field::{Field, Visit};
use tracing_subscriber::layer::Context;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{Layer, Registry};
#[derive(Debug, Default)]
pub(super) struct CapturedEvent {
fields: BTreeMap<String, String>,
}
impl CapturedEvent {
pub(super) fn field(&self, name: &str) -> &str {
self.fields.get(name).map_or("", String::as_str)
}
}
struct CaptureLayer(Arc<Mutex<Vec<CapturedEvent>>>);
impl<S> Layer<S> for CaptureLayer
where
S: tracing::Subscriber,
{
fn on_event(&self, event: &tracing::Event<'_>, _context: Context<'_, S>) {
let mut captured = CapturedEvent::default();
event.record(&mut FieldVisitor {
fields: &mut captured.fields,
});
if let Ok(mut events) = self.0.lock() {
events.push(captured);
}
}
}
struct FieldVisitor<'fields> {
fields: &'fields mut BTreeMap<String, String>,
}
impl Visit for FieldVisitor<'_> {
fn record_str(&mut self, field: &Field, value: &str) {
self.fields
.insert(field.name().to_owned(), value.to_owned());
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.fields
.insert(field.name().to_owned(), value.to_string());
}
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
self.fields
.insert(field.name().to_owned(), format!("{value:?}"));
}
}
pub(super) fn capture_events<R>(f: impl FnOnce() -> R) -> Vec<CapturedEvent> {
let events = Arc::new(Mutex::new(Vec::new()));
let subscriber = Registry::default().with(CaptureLayer(Arc::clone(&events)));
tracing::subscriber::with_default(subscriber, f);
events
.lock()
.map(|mut guard| std::mem::take(&mut *guard))
.unwrap_or_default()
}