tuwunel_core/log/capture/
layer.rs1use std::{fmt, sync::Arc};
7
8use arrayvec::ArrayVec;
9use tracing::field::{Field, Visit};
10use tracing_core::{Event, Subscriber};
11use tracing_subscriber::{layer::Context, registry::LookupSpan};
12
13use super::{Capture, Data, State};
14
15pub struct Layer {
20 state: Arc<State>,
21}
22
23struct Visitor {
24 values: Values,
25}
26
27type Values = ArrayVec<Value, 32>;
28pub type Value = (&'static str, String);
33
34type ScopeNames = ArrayVec<&'static str, 32>;
35
36impl Layer {
37 #[inline]
42 pub fn new(state: &Arc<State>) -> Self { Self { state: state.clone() } }
43}
44
45impl fmt::Debug for Layer {
46 #[inline]
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter.debug_struct("capture::Layer").finish()
49 }
50}
51
52impl<S> tracing_subscriber::Layer<S> for Layer
53where
54 S: Subscriber + for<'a> LookupSpan<'a>,
55{
56 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
57 self.state
58 .active
59 .read()
60 .expect("shared lock")
61 .iter()
62 .filter(|capture| filter(self, capture, event, &ctx))
63 .for_each(|capture| handle(self, capture, event, &ctx));
64 }
65}
66
67fn handle<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>)
68where
69 S: Subscriber + for<'a> LookupSpan<'a>,
70{
71 let names = ScopeNames::new();
72 let mut visitor = Visitor { values: Values::new() };
73 event.record(&mut visitor);
74
75 let mut closure = capture.closure.lock().expect("exclusive lock");
76 closure(Data {
77 layer,
78 event,
79 current: &ctx.current_span(),
80 values: &visitor.values,
81 scope: &names,
82 });
83}
84
85fn filter<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) -> bool
86where
87 S: Subscriber + for<'a> LookupSpan<'a>,
88{
89 let values = Values::new();
90 let mut names = ScopeNames::new();
91 if let Some(scope) = ctx.event_scope(event) {
92 for span in scope {
93 names.push(span.name());
94 }
95 }
96
97 capture.filter.as_ref().is_none_or(|filter| {
98 filter(Data {
99 layer,
100 event,
101 current: &ctx.current_span(),
102 values: &values,
103 scope: &names,
104 })
105 })
106}
107
108impl Visit for Visitor {
109 fn record_debug(&mut self, f: &Field, v: &dyn fmt::Debug) {
110 self.values.push((f.name(), format!("{v:?}")));
111 }
112
113 fn record_str(&mut self, f: &Field, v: &str) { self.values.push((f.name(), v.to_owned())); }
114}