use std::sync::{Arc, Mutex};
use tracing::Subscriber;
use tracing::span::{Attributes, Id};
use tracing_subscriber::Layer;
use tracing_subscriber::layer::Context;
#[derive(Debug, Clone)]
pub struct CapturedSpan {
pub name: String,
pub target: String,
pub level: tracing::Level,
pub fields: Vec<(String, String)>,
}
pub struct SpanCollector {
spans: Arc<Mutex<Vec<CapturedSpan>>>,
}
impl SpanCollector {
pub fn new() -> (Self, Arc<Mutex<Vec<CapturedSpan>>>) {
let spans = Arc::new(Mutex::new(Vec::new()));
(
Self {
spans: spans.clone(),
},
spans,
)
}
}
struct FieldVisitor {
fields: Vec<(String, String)>,
}
impl FieldVisitor {
fn new() -> Self {
Self { fields: Vec::new() }
}
}
impl tracing::field::Visit for FieldVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.fields
.push((field.name().to_string(), format!("{:?}", value)));
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
self.fields
.push((field.name().to_string(), value.to_string()));
}
fn record_i64(&mut self, field: &tracing::field::Field, value: i64) {
self.fields
.push((field.name().to_string(), value.to_string()));
}
fn record_u64(&mut self, field: &tracing::field::Field, value: u64) {
self.fields
.push((field.name().to_string(), value.to_string()));
}
fn record_bool(&mut self, field: &tracing::field::Field, value: bool) {
self.fields
.push((field.name().to_string(), value.to_string()));
}
}
impl<S: Subscriber> Layer<S> for SpanCollector {
fn on_new_span(&self, attrs: &Attributes<'_>, _id: &Id, _ctx: Context<'_, S>) {
let mut visitor = FieldVisitor::new();
attrs.record(&mut visitor);
let span = CapturedSpan {
name: attrs.metadata().name().to_string(),
target: attrs.metadata().target().to_string(),
level: *attrs.metadata().level(),
fields: visitor.fields,
};
if let Ok(mut spans) = self.spans.lock() {
spans.push(span);
}
}
}