1use tracing::{field::FieldSet, Event, Level, Metadata, Subscriber};
2use tracing_core::Kind;
3use tracing_subscriber::{
4 fmt::{format::Writer, FmtContext, FormatEvent, FormatFields},
5 registry::LookupSpan,
6};
7
8pub struct EventFormatter<const VISITOR_SIZE: usize, F, T> {
9 formatter: F,
10 check: T,
11}
12
13impl<const VISITOR_SIZE: usize, F, T> EventFormatter<VISITOR_SIZE, F, T>
14where
15 T: Fn(&Metadata<'static>) -> Option<Level> + Send + Sync,
16{
17 pub fn new(formatter: F, check: T) -> Self {
18 Self { formatter, check }
19 }
20}
21
22impl<const VISITOR_SIZE: usize, F, T, S, N> FormatEvent<S, N> for EventFormatter<VISITOR_SIZE, F, T>
23where
24 F: FormatEvent<S, N>,
25 T: Fn(&Metadata<'static>) -> Option<Level> + Send + Sync,
26 S: Subscriber + for<'a> LookupSpan<'a>,
27 N: for<'a> FormatFields<'a> + 'static,
28{
29 fn format_event(
30 &self,
31 ctx: &FmtContext<'_, S, N>,
32 writer: Writer<'_>,
33 event: &Event<'_>,
34 ) -> std::fmt::Result {
35 let metadata = event.metadata();
36
37 if let Some(level) = (self.check)(metadata) {
38 let kind = if metadata.is_event() {
39 Kind::EVENT
40 } else if metadata.is_span() {
41 Kind::SPAN
42 } else {
43 unreachable!()
44 };
45
46 let fields = metadata.fields();
47 let cloned = unsafe { std::mem::transmute_copy::<FieldSet, FieldSet>(fields) };
64
65 let metadata = Box::leak::<'static>(Box::new(Metadata::new(
67 metadata.name(),
68 metadata.target(),
69 level,
70 metadata.file(),
71 metadata.line(),
72 metadata.module_path(),
73 cloned,
74 kind,
75 )));
76
77 let mut visitor = visitor::Visitor::<VISITOR_SIZE>::new();
78 event.record(&mut visitor);
79 let values = visitor.get_values();
80 let valueset = fields.value_set(&values);
81 let event = if let Some(parent) = event.parent() {
82 Event::new_child_of(parent, metadata, &valueset)
83 } else {
84 Event::new(metadata, &valueset)
85 };
86 let res = self.formatter.format_event(ctx, writer, &event);
87
88 #[cfg(not(feature = "i_really_want_memory_leak"))]
94 drop(unsafe { Box::from_raw(metadata as *const Metadata as *mut Metadata) });
95
96 res
97 } else {
98 self.formatter.format_event(ctx, writer, event)
99 }
100 }
101}
102
103mod visitor {
104 use std::fmt::Debug;
105
106 use tracing::{field::Visit, Level, Metadata, Value};
107 use tracing_core::{metadata, Callsite, Field, Interest, Kind};
108
109 const FAKE_FIELD_NAME: &str = "foo";
110
111 struct FakeCallSite();
113 static FAKE_CALLSITE: FakeCallSite = FakeCallSite();
114 static FAKE_META: Metadata<'static> = metadata! {
115 name: "",
116 target: module_path!(),
117 level: Level::INFO,
118 fields: &[FAKE_FIELD_NAME],
119 callsite: &FAKE_CALLSITE,
120 kind: Kind::SPAN,
121 };
122
123 impl Callsite for FakeCallSite {
124 fn set_interest(&self, _: Interest) {
125 unimplemented!()
126 }
127
128 fn metadata(&self) -> &Metadata<'_> {
129 &FAKE_META
130 }
131 }
132
133 pub struct Visitor<const N: usize> {
134 index: usize,
135 values: [(Field, Option<String>); N],
137 }
138
139 impl<const N: usize> Visitor<N> {
140 pub fn new() -> Self {
141 Visitor {
142 index: 0,
143 values: [(); N].map(|_| (FAKE_META.fields().field(FAKE_FIELD_NAME).unwrap(), None)),
144 }
145 }
146
147 pub fn get_values(&self) -> [(&Field, Option<&dyn Value>); N] {
148 let mut index = 0;
149 [(); N].map(|_| {
150 let val = (
151 &self.values[index].0,
152 self.values[index].1.as_ref().map(|s| s as &dyn Value),
153 );
154 index += 1;
155 val
156 })
157 }
158 }
159
160 impl<const N: usize> Visit for Visitor<N> {
161 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
162 let cloned = unsafe { std::mem::transmute_copy::<Field, Field>(field) };
171 self.values[self.index] = (cloned, Some(format!("{value:?}")));
172 self.index += 1;
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use tracing::{Level, Metadata};
180 use tracing_subscriber::{
181 fmt,
182 util::{SubscriberInitExt, TryInitError},
183 EnvFilter,
184 };
185
186 fn init_tracing(
187 check: impl Fn(&Metadata<'static>) -> Option<Level> + Send + Sync + 'static,
188 ) -> Result<(), TryInitError> {
189 let format = fmt::format()
190 .with_target(true)
191 .with_line_number(true)
192 .with_thread_ids(true)
193 .compact();
194
195 #[cfg(miri)]
197 let format = format.without_time();
198
199 let builder = fmt::Subscriber::builder();
200
201 builder
202 .with_env_filter(EnvFilter::from_default_env())
203 .event_format(format)
204 .map_event_format(|formatter| super::EventFormatter::<10, _, _>::new(formatter, check))
205 .finish()
206 .try_init()
207 }
208
209 #[test]
210 fn miri_tracing() {
211 init_tracing(|metadata| {
212 (dbg!(metadata.file()).is_some_and(|file| file == "src/lib.rs")
213 && Level::ERROR.eq(metadata.level()))
214 .then_some(Level::WARN)
215 })
216 .unwrap();
217
218 tracing::error!("test");
219 }
220}