soma_test_support/
tracing_capture.rs1use std::io::Write;
15use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
16
17pub fn tracing_test_lock() -> MutexGuard<'static, ()> {
20 static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
21 LOCK.get_or_init(|| Mutex::new(()))
22 .lock()
23 .unwrap_or_else(std::sync::PoisonError::into_inner)
24}
25
26#[derive(Clone, Default)]
28pub struct SharedBuf(Arc<Mutex<Vec<u8>>>);
29
30impl SharedBuf {
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn contents(&self) -> String {
37 let bytes = self
38 .0
39 .lock()
40 .unwrap_or_else(std::sync::PoisonError::into_inner);
41 String::from_utf8_lossy(&bytes).into_owned()
42 }
43
44 pub fn writer(&self) -> SharedWriter {
46 SharedWriter(self.0.clone())
47 }
48}
49
50#[derive(Clone)]
52pub struct SharedWriter(Arc<Mutex<Vec<u8>>>);
53
54impl Write for SharedWriter {
55 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
56 self.0
57 .lock()
58 .unwrap_or_else(std::sync::PoisonError::into_inner)
59 .extend_from_slice(buf);
60 Ok(buf.len())
61 }
62
63 fn flush(&mut self) -> std::io::Result<()> {
64 Ok(())
65 }
66}
67
68impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for SharedWriter {
69 type Writer = SharedWriter;
70
71 fn make_writer(&'a self) -> Self::Writer {
72 self.clone()
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn captures_emitted_events() {
82 let _lock = tracing_test_lock();
83 let buf = SharedBuf::new();
84 let subscriber = tracing_subscriber::fmt()
85 .with_writer(buf.writer())
86 .with_ansi(false)
87 .without_time()
88 .finish();
89 tracing::subscriber::with_default(subscriber, || {
90 tracing::info!(surface = "test", "captured event");
91 });
92 let logs = buf.contents();
93 assert!(logs.contains("captured event"), "logs were: {logs}");
94 assert!(logs.contains("surface"), "logs were: {logs}");
95 }
96}