use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tracing::Subscriber;
use tracing::field::{Field, Visit};
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::registry::LookupSpan;
#[derive(Clone, Default)]
pub struct EventCapture {
events: Arc<Mutex<Vec<CapturedEvent>>>,
}
#[derive(Debug, Clone)]
pub struct CapturedEvent {
pub name: String,
pub target: String,
pub level: String,
pub fields: HashMap<String, String>,
}
impl EventCapture {
pub fn snapshot(&self) -> Vec<CapturedEvent> {
self.events
.lock()
.unwrap_or_else(|p| p.into_inner())
.clone()
}
pub fn revocation_events(&self) -> Vec<CapturedEvent> {
self.snapshot()
.into_iter()
.filter(|e| e.target == "ppoppo_token::revocation")
.collect()
}
}
#[derive(Default)]
struct FieldVisitor {
fields: HashMap<String, String>,
}
impl Visit for FieldVisitor {
fn record_str(&mut self, field: &Field, value: &str) {
self.fields.insert(field.name().to_string(), value.to_string());
}
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
self.fields
.insert(field.name().to_string(), format!("{value:?}"));
}
fn record_i64(&mut self, field: &Field, value: i64) {
self.fields.insert(field.name().to_string(), value.to_string());
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.fields.insert(field.name().to_string(), value.to_string());
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.fields.insert(field.name().to_string(), value.to_string());
}
}
impl<S> Layer<S> for EventCapture
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
let metadata = event.metadata();
let mut visitor = FieldVisitor::default();
event.record(&mut visitor);
self.events
.lock()
.unwrap_or_else(|p| p.into_inner())
.push(CapturedEvent {
name: metadata.name().to_string(),
target: metadata.target().to_string(),
level: metadata.level().to_string(),
fields: visitor.fields,
});
}
}