1use std::ffi::c_char;
2use std::io::Write;
3use std::sync::{Arc, OnceLock};
4
5use arc_swap::ArcSwap;
6use tracing_subscriber::layer::SubscriberExt;
7use tracing_subscriber::util::SubscriberInitExt;
8use tracing_subscriber::{EnvFilter, Registry};
9
10use crate::config::LoggingConfig;
11
12pub type LogCallback = extern "C" fn(message: *const c_char);
14
15static LOG_CALLBACK: OnceLock<ArcSwap<Option<LogCallback>>> = OnceLock::new();
17
18fn get_log_callback_handle() -> &'static ArcSwap<Option<LogCallback>> {
21 LOG_CALLBACK.get_or_init(|| ArcSwap::from(Arc::new(None)))
22}
23
24struct FfiWriter;
26
27impl Write for FfiWriter {
28 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
29 let callback_handle = get_log_callback_handle();
30 let callback_arc = callback_handle.load();
31 if let Some(callback) = **callback_arc {
32 let message_bytes = if buf.last() == Some(&b'\n') {
34 &buf[..buf.len() - 1]
35 } else {
36 buf
37 };
38
39 if let Ok(message_cstr) = std::ffi::CString::new(message_bytes) {
40 callback(message_cstr.as_ptr());
41 }
42 }
43 Ok(buf.len())
44 }
45
46 fn flush(&mut self) -> std::io::Result<()> {
47 Ok(())
48 }
49}
50
51pub fn init_for_binary(config: &LoggingConfig) {
55 let filter = EnvFilter::try_from_default_env()
56 .unwrap_or_else(|_| EnvFilter::new(config.log_level.as_deref().unwrap_or("info")));
57
58 let (non_blocking_writer, _guard) = tracing_appender::non_blocking(std::io::stdout());
59 std::mem::forget(_guard); let layer = tracing_subscriber::fmt::layer()
62 .with_writer(non_blocking_writer)
63 .with_thread_ids(true);
64
65 Registry::default().with(filter).with(layer).init();
66}
67
68pub fn init_for_ffi(config: &LoggingConfig) {
72 let filter = EnvFilter::try_from_default_env()
73 .unwrap_or_else(|_| EnvFilter::new(config.log_level.as_deref().unwrap_or("info")));
74
75 let (ffi_writer, _guard) = tracing_appender::non_blocking(FfiWriter);
76 std::mem::forget(_guard); let layer = tracing_subscriber::fmt::layer()
79 .with_writer(ffi_writer)
80 .with_ansi(false) .with_target(true)
82 .with_thread_ids(true)
83 .with_level(true);
84
85 Registry::default().with(filter).with(layer).init();
86}
87
88pub fn set_log_callback(callback: Option<LogCallback>) {
92 let callback_handle = get_log_callback_handle();
93 callback_handle.store(Arc::new(callback));
94}