dns2socks_core/
dump_logger.rs

1use crate::ArgVerbosity;
2use std::os::raw::{c_char, c_void};
3
4static DUMP_CALLBACK: std::sync::OnceLock<Option<DumpCallback>> = std::sync::OnceLock::new();
5
6/// # Safety
7///
8/// set dump log info callback.
9#[unsafe(no_mangle)]
10pub unsafe extern "C" fn dns2socks_set_log_callback(
11    callback: Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>,
12    ctx: *mut c_void,
13) {
14    if let Some(_cb) = DUMP_CALLBACK.get_or_init(|| Some(DumpCallback(callback, ctx))) {
15        log::info!("dump log callback set success");
16    } else {
17        log::warn!("dump log callback already set");
18    }
19}
20
21#[derive(Clone, Debug)]
22struct DumpCallback(Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>, *mut c_void);
23
24impl DumpCallback {
25    unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) {
26        if let Some(cb) = self.0 {
27            unsafe { cb(dump_level, info, self.1) };
28        }
29    }
30}
31
32unsafe impl Send for DumpCallback {}
33unsafe impl Sync for DumpCallback {}
34
35#[derive(Debug, Clone, PartialEq, Eq, Default)]
36pub(crate) struct DumpLogger;
37
38impl log::Log for DumpLogger {
39    fn enabled(&self, metadata: &log::Metadata) -> bool {
40        metadata.level() <= log::Level::Trace
41    }
42
43    fn log(&self, record: &log::Record) {
44        if self.enabled(record.metadata()) {
45            let current_crate_name = env!("CARGO_CRATE_NAME");
46            if record.module_path().unwrap_or("").starts_with(current_crate_name) {
47                self.do_dump_log(record);
48            }
49        }
50    }
51
52    fn flush(&self) {}
53}
54
55impl DumpLogger {
56    fn do_dump_log(&self, record: &log::Record) {
57        let timestamp: chrono::DateTime<chrono::Local> = chrono::Local::now();
58        let msg = format!(
59            "[{} {:<5} {}] - {}",
60            timestamp.format("%Y-%m-%d %H:%M:%S"),
61            record.level(),
62            record.module_path().unwrap_or(""),
63            record.args()
64        );
65        let Ok(c_msg) = std::ffi::CString::new(msg) else {
66            return;
67        };
68        let ptr = c_msg.as_ptr();
69        if let Some(Some(cb)) = DUMP_CALLBACK.get() {
70            unsafe { cb.clone().call(record.level().into(), ptr) };
71        }
72    }
73}