dns2socks_core/
dump_logger.rs1use 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#[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}