cfg_if::cfg_if! { if #[cfg(feature = "tauri-dev")] { use std::fmt; } else { #[cfg(feature = "verge-dev")] use nu_ansi_term::Color; use std::{fmt, io::Write, thread}; use flexi_logger::DeferredNow; use log::{LevelFilter, Record}; use flexi_logger::filter::LogLineFilter; } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Type { Cmd, Core, Config, Setup, System, Service, Hotkey, Window, Tray, Timer, Frontend, Backup, Lightweight, Network, ProxyMode, // Ipc, // Cache, ClashVergeRev, } impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Type::Cmd => write!(f, "[Cmd]"), Type::Core => write!(f, "[Core]"), Type::Config => write!(f, "[Config]"), Type::Setup => write!(f, "[Setup]"), Type::System => write!(f, "[System]"), Type::Service => write!(f, "[Service]"), Type::Hotkey => write!(f, "[Hotkey]"), Type::Window => write!(f, "[Window]"), Type::Tray => write!(f, "[Tray]"), Type::Timer => write!(f, "[Timer]"), Type::Frontend => write!(f, "[Frontend]"), Type::Backup => write!(f, "[Backup]"), Type::Lightweight => write!(f, "[Lightweight]"), Type::Network => write!(f, "[Network]"), Type::ProxyMode => write!(f, "[ProxMode]"), // Type::Ipc => write!(f, "[IPC]"), // Type::Cache => write!(f, "[Cache]"), Type::ClashVergeRev => write!(f, "[ClashVergeRev]"), } } } #[macro_export] macro_rules! error { ($result: expr) => { log::error!(target: "app", "{}", $result); }; } #[macro_export] macro_rules! log_err { ($result: expr) => { if let Err(err) = $result { log::error!(target: "app", "{err}"); } }; ($result: expr, $err_str: expr) => { if let Err(_) = $result { log::error!(target: "app", "{}", $err_str); } }; } #[macro_export] macro_rules! trace_err { ($result: expr, $err_str: expr) => { if let Err(err) = $result { log::trace!(target: "app", "{}, err {}", $err_str, err); } } } /// wrap the anyhow error /// transform the error to String #[macro_export] macro_rules! wrap_err { // Case 1: Future> ($stat:expr, async) => {{ match $stat.await { Ok(a) => Ok(a), Err(err) => { log::error!(target: "app", "{}", err); Err(err.to_string()) } } }}; // Case 2: Result ($stat:expr) => {{ match $stat { Ok(a) => Ok(a), Err(err) => { log::error!(target: "app", "{}", err); Err(err.to_string()) } } }}; } #[macro_export] macro_rules! logging { // 带 println 的版本(支持格式化参数) ($level:ident, $type:expr, true, $($arg:tt)*) => { // We dont need println here anymore // println!("{} {}", $type, format_args!($($arg)*)); log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*)); }; // 带 println 的版本(使用 false 明确不打印) ($level:ident, $type:expr, false, $($arg:tt)*) => { log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*)); }; // 不带 print 参数的版本(默认不打印) ($level:ident, $type:expr, $($arg:tt)*) => { log::$level!(target: "app", "{} {}", $type, format_args!($($arg)*)); }; } #[macro_export] macro_rules! logging_error { // 1. 处理 Result,带打印控制 ($type:expr, $print:expr, $expr:expr) => { match $expr { Ok(_) => {}, Err(err) => { if $print { println!("[{}] Error: {}", $type, err); } log::error!(target: "app", "[{}] {}", $type, err); } } }; // 2. 处理 Result,默认不打印 ($type:expr, $expr:expr) => { if let Err(err) = $expr { log::error!(target: "app", "[{}] {}", $type, err); } }; // 3. 处理格式化字符串,带打印控制 ($type:expr, $print:expr, $fmt:literal $(, $arg:expr)*) => { if $print { println!("[{}] {}", $type, format_args!($fmt $(, $arg)*)); } log::error!(target: "app", "[{}] {}", $type, format_args!($fmt $(, $arg)*)); }; // 4. 处理格式化字符串,不带 bool 时,默认 `false` ($type:expr, $fmt:literal $(, $arg:expr)*) => { logging_error!($type, false, $fmt $(, $arg)*); }; } #[cfg(not(feature = "tauri-dev"))] static IGNORE_MODULES: &[&str] = &["tauri", "wry"]; #[cfg(not(feature = "tauri-dev"))] pub struct NoExternModule; #[cfg(not(feature = "tauri-dev"))] impl LogLineFilter for NoExternModule { fn write( &self, now: &mut DeferredNow, record: &Record, log_line_writer: &dyn flexi_logger::filter::LogLineWriter, ) -> std::io::Result<()> { let module_path = record.module_path().unwrap_or_default(); if IGNORE_MODULES.iter().any(|m| module_path.starts_with(m)) { Ok(()) } else { log_line_writer.write(now, record) } } } #[cfg(not(feature = "tauri-dev"))] pub fn get_log_level(log_level: &LevelFilter) -> String { #[cfg(feature = "verge-dev")] match log_level { LevelFilter::Off => Color::Fixed(8).paint("OFF").to_string(), LevelFilter::Error => Color::Red.paint("ERROR").to_string(), LevelFilter::Warn => Color::Yellow.paint("WARN ").to_string(), LevelFilter::Info => Color::Green.paint("INFO ").to_string(), LevelFilter::Debug => Color::Blue.paint("DEBUG").to_string(), LevelFilter::Trace => Color::Purple.paint("TRACE").to_string(), } #[cfg(not(feature = "verge-dev"))] log_level.to_string() } #[cfg(not(feature = "tauri-dev"))] pub fn console_colored_format( w: &mut dyn Write, now: &mut DeferredNow, record: &log::Record, ) -> std::io::Result<()> { let current_thread = thread::current(); let thread_name = current_thread.name().unwrap_or("unnamed"); let level = get_log_level(&record.level().to_level_filter()); let line = record.line().unwrap_or(0); write!( w, "[{}] {} [{}:{}] T[{}] {}", now.format("%H:%M:%S%.3f"), level, record.module_path().unwrap_or(""), line, thread_name, record.args(), ) } #[cfg(not(feature = "tauri-dev"))] pub fn file_format( w: &mut dyn Write, now: &mut DeferredNow, record: &Record, ) -> std::io::Result<()> { write!( w, "[{}] {} {}", now.format("%Y-%m-%d %H:%M:%S%.3f"), record.level(), record.args(), ) }