use std::{fmt, sync::Arc}; use arrayvec::ArrayVec; use tracing::field::{Field, Visit}; use tracing_core::{Event, Subscriber}; use tracing_subscriber::{layer::Context, registry::LookupSpan}; use super::{Capture, Data, State}; pub struct Layer { state: Arc, } struct Visitor { values: Values, } type Values = ArrayVec; pub type Value = (&'static str, String); type ScopeNames = ArrayVec<&'static str, 32>; impl Layer { #[inline] pub fn new(state: &Arc) -> Self { Self { state: state.clone() } } } impl fmt::Debug for Layer { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.debug_struct("capture::Layer").finish() } } impl tracing_subscriber::Layer for Layer where S: Subscriber + for<'a> LookupSpan<'a>, { fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { self.state .active .read() .expect("shared lock") .iter() .filter(|capture| filter(self, capture, event, &ctx)) .for_each(|capture| handle(self, capture, event, &ctx)); } } fn handle(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) where S: Subscriber + for<'a> LookupSpan<'a>, { let names = ScopeNames::new(); let mut visitor = Visitor { values: Values::new() }; event.record(&mut visitor); let mut closure = capture.closure.lock().expect("exclusive lock"); closure(Data { layer, event, current: &ctx.current_span(), values: &visitor.values, scope: &names, }); } fn filter(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) -> bool where S: Subscriber + for<'a> LookupSpan<'a>, { let values = Values::new(); let mut names = ScopeNames::new(); if let Some(scope) = ctx.event_scope(event) { for span in scope { names.push(span.name()); } } capture.filter.as_ref().is_none_or(|filter| { filter(Data { layer, event, current: &ctx.current_span(), values: &values, scope: &names, }) }) } impl Visit for Visitor { fn record_debug(&mut self, f: &Field, v: &dyn fmt::Debug) { self.values.push((f.name(), format!("{v:?}"))); } fn record_str(&mut self, f: &Field, v: &str) { self.values.push((f.name(), v.to_owned())); } }