use crate::{ArgVerbosity, BoxError};
use std::{
os::raw::{c_char, c_void},
sync::Mutex,
};
static DUMP_CALLBACK: Mutex<Option<DumpCallback>> = Mutex::new(None);
static LOGGER_SETTED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
pub(crate) fn check_logger() -> bool {
LOGGER_SETTED.load(std::sync::atomic::Ordering::SeqCst)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn overtls_set_log_callback(
set_logger: bool,
callback: Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>,
ctx: *mut c_void,
) {
if set_logger {
LOGGER_SETTED.store(true, std::sync::atomic::Ordering::Relaxed);
log::set_max_level(log::LevelFilter::Trace);
if let Err(err) = log::set_boxed_logger(Box::<DumpLogger>::default()) {
log::warn!("failed to set logger, error={err:?}");
}
}
if let Ok(mut cb) = DUMP_CALLBACK.lock() {
*cb = Some(DumpCallback(callback, ctx));
} else {
log::error!("set log callback failed");
}
}
#[derive(Clone)]
struct DumpCallback(Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>, *mut c_void);
impl DumpCallback {
unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) {
if let Some(cb) = self.0 {
unsafe { cb(dump_level, info, self.1) };
}
}
}
unsafe impl Send for DumpCallback {}
unsafe impl Sync for DumpCallback {}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct DumpLogger;
impl log::Log for DumpLogger {
fn enabled(&self, metadata: &log::Metadata) -> bool {
metadata.level() <= log::Level::Trace
}
fn log(&self, record: &log::Record) {
#[cfg(not(target_os = "ios"))]
if self.enabled(record.metadata()) {
let current_crate_name = env!("CARGO_CRATE_NAME");
if record.module_path().unwrap_or("").starts_with(current_crate_name) {
if let Err(err) = self.do_dump_log(record) {
eprint!("failed to dump log, error={err:?}");
}
}
}
#[cfg(target_os = "ios")]
if self.enabled(record.metadata()) {
let module = record.module_path().unwrap_or("");
if module.starts_with("rustls") || module.starts_with("tungstenite") || module.starts_with("tokio_tungstenite") {
return;
}
if let Err(err) = self.do_dump_log(record) {
eprint!("failed to dump log, error={err:?}");
}
}
}
fn flush(&self) {}
}
impl DumpLogger {
fn do_dump_log(&self, record: &log::Record) -> Result<(), BoxError> {
let _timestamp: chrono::DateTime<chrono::Local> = chrono::Local::now();
#[cfg(not(target_os = "ios"))]
let msg = format!(
"[{} {:<5} {}] - {}",
_timestamp.format("%Y-%m-%d %H:%M:%S"),
record.level(),
record.module_path().unwrap_or(""),
record.args()
);
#[cfg(target_os = "ios")]
let msg = format!("[{:<5} {}] - {}", record.level(), record.module_path().unwrap_or(""), record.args());
let c_msg = std::ffi::CString::new(msg)?;
let ptr = c_msg.as_ptr();
if let Ok(cb) = DUMP_CALLBACK.lock() {
if let Some(cb) = cb.clone() {
unsafe { cb.call(record.level().into(), ptr) };
}
}
Ok(())
}
}