Skip to main content

tuwunel_core/log/capture/
layer.rs

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