diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 93fde36528..c6311c008a 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -689,7 +689,6 @@ dependencies = [ "gunzip", "indexmap 2.2.3", "log", - "log4rs", "nanoid", "once_cell", "open 5.0.1", @@ -714,6 +713,13 @@ dependencies = [ "tempfile", "thiserror", "tokio", + "tracing", + "tracing-appender", + "tracing-attributes", + "tracing-error", + "tracing-futures", + "tracing-log", + "tracing-subscriber", "warp", "which 6.0.0", "window-shadows", @@ -1115,12 +1121,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "destructure_traitobject" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" - [[package]] name = "digest" version = "0.9.0" @@ -1424,6 +1424,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.0.28" @@ -2144,12 +2150,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.28" @@ -2703,43 +2703,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "serde", -] - -[[package]] -name = "log-mdc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" - -[[package]] -name = "log4rs" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" -dependencies = [ - "anyhow", - "arc-swap", - "chrono", - "derivative", - "fnv", - "humantime", - "libc", - "log", - "log-mdc", - "once_cell", - "parking_lot", - "rand 0.8.5", - "serde", - "serde-value", - "serde_json", - "serde_yaml 0.9.32", - "thiserror", - "thread-id", - "typemap-ors", - "winapi", -] [[package]] name = "loom" @@ -3372,10 +3335,13 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ + "backtrace", "cfg-if", "libc", + "petgraph", "redox_syscall", "smallvec", + "thread-id", "windows-targets 0.48.5", ] @@ -3469,6 +3435,16 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.2.3", +] + [[package]] name = "phf" version = "0.8.0" @@ -4453,16 +4429,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - [[package]] name = "serde_derive" version = "1.0.197" @@ -5620,6 +5586,19 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "parking_lot", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" @@ -5641,6 +5620,26 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -5652,6 +5651,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -5661,13 +5670,17 @@ dependencies = [ "matchers", "nu-ansi-term", "once_cell", + "parking_lot", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -5715,15 +5728,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "typemap-ors" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" -dependencies = [ - "unsafe-any-ors", -] - [[package]] name = "typenum" version = "1.17.0" @@ -5783,15 +5787,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" -[[package]] -name = "unsafe-any-ors" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" -dependencies = [ - "destructure_traitobject", -] - [[package]] name = "unsafe-libyaml" version = "0.2.10" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index f633c8d5ac..3e54705784 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -10,6 +10,7 @@ authors = ["zzzgydi", "keiko233"] [workspace.dependencies] thiserror = "1" +tracing = "0.1" [profile.release] panic = "abort" diff --git a/backend/tauri/Cargo.toml b/backend/tauri/Cargo.toml index bd4f91e89c..b9c7bafa40 100644 --- a/backend/tauri/Cargo.toml +++ b/backend/tauri/Cargo.toml @@ -23,7 +23,6 @@ open = "5.0.1" log = "0.4.20" ctrlc = "3.4.2" dunce = "1.0.4" -log4rs = "1.2.0" nanoid = "0.4.0" chrono = "0.4.31" sysinfo = "0.30" @@ -35,7 +34,7 @@ auto-launch = "0.5" once_cell = "1.19.0" port_scanner = "0.1.5" delay_timer = "0.11.5" -parking_lot = "0.12.1" +parking_lot = { version = "0.12.1" } tokio = { version = "1", features = ["full"] } serde = { version = "1.0", features = ["derive"] } reqwest = { version = "0.11", features = ["json", "rustls-tls"] } @@ -70,6 +69,17 @@ rust-i18n = "3" adler = "1.0.2" rfd = "0.10" # should bump to v0.14 when clarify why the rfd v0.10 from tauri breaks build indexmap = { version = "2.2.3", features = ["serde"] } +tracing = { workspace = true } +tracing-attributes = "0.1" +tracing-futures = "0.2" +tracing-subscriber = { version = "0.3", features = [ + "env-filter", + "json", + "parking_lot", +] } +tracing-error = "0.2" +tracing-log = { version = "0.2" } +tracing-appender = { version = "0.2", features = ["parking_lot"] } [target.'cfg(windows)'.dependencies] deelevate = "0.2.0" @@ -112,3 +122,4 @@ custom-protocol = ["tauri/custom-protocol"] verge-dev = [] default-meta = [] nightly = [] +deadlock-detection = ["parking_lot/deadlock_detection"] diff --git a/backend/tauri/src/config/mod.rs b/backend/tauri/src/config/mod.rs index 0d63587d58..bf51e7e5ed 100644 --- a/backend/tauri/src/config/mod.rs +++ b/backend/tauri/src/config/mod.rs @@ -1,9 +1,9 @@ mod clash; mod core; mod draft; +mod nyanpasu; mod prfitem; mod profiles; mod runtime; -mod verge; -pub use self::{clash::*, core::*, draft::*, prfitem::*, profiles::*, runtime::*, verge::*}; +pub use self::{clash::*, core::*, draft::*, nyanpasu::*, prfitem::*, profiles::*, runtime::*}; diff --git a/backend/tauri/src/config/nyanpasu/logging.rs b/backend/tauri/src/config/nyanpasu/logging.rs new file mode 100644 index 0000000000..35181fe10d --- /dev/null +++ b/backend/tauri/src/config/nyanpasu/logging.rs @@ -0,0 +1,49 @@ +use super::IVerge; +use serde::{Deserialize, Serialize}; +use tracing_subscriber::filter; +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum LoggingLevel { + #[serde(rename = "silent", alias = "off")] + Silent, + #[serde(rename = "trace", alias = "tracing")] + Trace, + #[serde(rename = "debug")] + Debug, + #[serde(rename = "info")] + Info, + #[serde(rename = "warn", alias = "warning")] + Warn, + #[serde(rename = "error")] + Error, +} + +impl Default for LoggingLevel { + #[cfg(debug_assertions)] + fn default() -> Self { + Self::Trace + } + + #[cfg(not(debug_assertions))] + fn default() -> Self { + Self::Info + } +} + +impl From for filter::LevelFilter { + fn from(level: LoggingLevel) -> Self { + match level { + LoggingLevel::Silent => filter::LevelFilter::OFF, + LoggingLevel::Trace => filter::LevelFilter::TRACE, + LoggingLevel::Debug => filter::LevelFilter::DEBUG, + LoggingLevel::Info => filter::LevelFilter::INFO, + LoggingLevel::Warn => filter::LevelFilter::WARN, + LoggingLevel::Error => filter::LevelFilter::ERROR, + } + } +} + +impl IVerge { + pub fn get_log_level(&self) -> LoggingLevel { + self.app_log_level.clone().unwrap_or_default() + } +} diff --git a/backend/tauri/src/config/verge.rs b/backend/tauri/src/config/nyanpasu/mod.rs similarity index 89% rename from backend/tauri/src/config/verge.rs rename to backend/tauri/src/config/nyanpasu/mod.rs index 7d47eec15b..a95f9166b4 100644 --- a/backend/tauri/src/config/verge.rs +++ b/backend/tauri/src/config/nyanpasu/mod.rs @@ -1,8 +1,10 @@ use crate::utils::{dirs, help}; use anyhow::Result; -use log::LevelFilter; +// use log::LevelFilter; use serde::{Deserialize, Serialize}; +pub mod logging; + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)] pub enum ClashCore { #[serde(rename = "clash", alias = "clash-premium")] @@ -54,7 +56,7 @@ pub struct IVerge { /// app log level /// silent | error | warn | info | debug | trace - pub app_log_level: Option, + pub app_log_level: Option, // i18n pub language: Option, @@ -131,8 +133,10 @@ pub struct IVerge { /// 日志清理 /// 分钟数; 0 为不清理 + #[deprecated(note = "use `window_size_state` instead")] pub auto_log_clean: Option, - + /// 日记轮转时间,单位:天 + pub max_log_files: Option, /// window size and position #[deprecated(note = "use `window_size_state` instead")] #[serde(skip_serializing_if = "Option::is_none")] @@ -194,10 +198,7 @@ impl IVerge { let locale = crate::utils::help::get_system_locale(); Some(crate::utils::help::mapping_to_i18n_key(&locale).into()) }, - #[cfg(debug_assertions)] - app_log_level: Some("debug".into()), - #[cfg(not(debug_assertions))] - app_log_level: Some("info".into()), + app_log_level: Some(logging::LoggingLevel::default()), theme_mode: Some("system".into()), theme_blur: Some(false), traffic_graph: Some(true), @@ -213,7 +214,8 @@ impl IVerge { enable_builtin_enhanced: Some(true), enable_clash_fields: Some(true), page_transition_animation: Some("slide".into()), - auto_log_clean: Some(60 * 24 * 7), // 7 days 自动清理日记 + // auto_log_clean: Some(60 * 24 * 7), // 7 days 自动清理日记 + max_log_files: Some(7), // 7 days ..Self::default() } } @@ -264,7 +266,7 @@ impl IVerge { patch!(proxy_layout_column); patch!(enable_clash_fields); - patch!(auto_log_clean); + patch!(max_log_files); patch!(window_size_state); } @@ -280,21 +282,4 @@ impl IVerge { Err(_) => SERVER_PORT, // 这里就不log错误了 } } - - /// 获取日志等级 - pub fn get_log_level(&self) -> LevelFilter { - if let Some(level) = self.app_log_level.as_ref() { - match level.to_lowercase().as_str() { - "silent" => LevelFilter::Off, - "error" => LevelFilter::Error, - "warn" => LevelFilter::Warn, - "info" => LevelFilter::Info, - "debug" => LevelFilter::Debug, - "trace" => LevelFilter::Trace, - _ => LevelFilter::Info, - } - } else { - LevelFilter::Info - } - } } diff --git a/backend/tauri/src/core/tasks/jobs/mod.rs b/backend/tauri/src/core/tasks/jobs/mod.rs index d078d6644e..4e4c87ebc2 100644 --- a/backend/tauri/src/core/tasks/jobs/mod.rs +++ b/backend/tauri/src/core/tasks/jobs/mod.rs @@ -25,8 +25,9 @@ impl JobsManager { } pub fn global_register() -> Result<()> { - let jobs: Vec> = - vec![Box::::default() as Box]; + let jobs: Vec> = vec![ + // Box::::default() as Box + ]; for job in jobs { let task = job.setup(); if let Some(task) = task { diff --git a/backend/tauri/src/core/tray/proxies.rs b/backend/tauri/src/core/tray/proxies.rs index 402c8ed9a3..6df42c8cdb 100644 --- a/backend/tauri/src/core/tray/proxies.rs +++ b/backend/tauri/src/core/tray/proxies.rs @@ -3,23 +3,25 @@ use crate::core::{ handle::Handle, }; use indexmap::IndexMap; -use log::{debug, error, warn}; use tauri::SystemTrayMenu; +use tracing::{debug, error, warn}; +use tracing_attributes::instrument; +#[instrument] async fn loop_task() { loop { match ProxiesGuard::global().update().await { Ok(_) => { - debug!(target: "tray", "update proxies success"); + debug!("update proxies success"); } Err(e) => { - warn!(target: "tray", "update proxies failed: {:?}", e); + warn!("update proxies failed: {:?}", e); } } { let guard = ProxiesGuard::global().read(); if guard.updated_at() == 0 { - error!(target: "tray", "proxies not updated yet!!!!"); + error!("proxies not updated yet!!!!"); // TODO: add a error dialog or notification, and panic? } @@ -130,7 +132,7 @@ fn diff_proxies(old_proxies: &TrayProxies, new_proxies: &TrayProxies) -> TrayUpd TrayUpdateType::Part(actions) } } - +#[instrument] pub async fn proxies_updated_receiver() { let (mut rx, mut tray_proxies_holder) = { let guard = ProxiesGuard::global().read(); @@ -145,9 +147,9 @@ pub async fn proxies_updated_receiver() { loop { match rx.recv().await { Ok(_) => { - debug!(target: "tray::proxies", "proxies updated"); + debug!("proxies updated"); if Handle::global().app_handle.lock().is_none() { - warn!(target: "tray::proxies", "app handle not found"); + warn!("app handle not found"); continue; } Handle::mutate_proxies(); @@ -158,28 +160,28 @@ pub async fn proxies_updated_receiver() { match diff_proxies(&tray_proxies_holder, ¤t_tray_proxies) { TrayUpdateType::Full => { - debug!(target: "tray::proxies", "should do full update"); + debug!("should do full update"); tray_proxies_holder = current_tray_proxies; match Handle::update_systray() { Ok(_) => { - debug!(target: "tray::proxies", "update systray success"); + debug!("update systray success"); } Err(e) => { - warn!(target: "tray::proxies", "update systray failed: {:?}", e); + warn!("update systray failed: {:?}", e); } } } TrayUpdateType::Part(action_list) => { - debug!(target: "tray::proxies", "should do partial update, op list: {:?}", action_list); + debug!("should do partial update, op list: {:?}", action_list); tray_proxies_holder = current_tray_proxies; platform_impl::update_selected_proxies(&action_list); - debug!(target: "tray::proxies", "update selected proxies success"); + debug!("update selected proxies success"); } _ => {} } } Err(e) => { - warn!(target: "tray::proxies", "proxies updated receiver failed: {:?}", e); + warn!("proxies updated receiver failed: {:?}", e); } } } @@ -261,6 +263,7 @@ impl SystemTrayMenuProxiesExt for SystemTrayMenu { } } +#[instrument] pub fn on_system_tray_event(event: &str) { if !event.starts_with("select_proxy_") { return; // bypass non-select event @@ -274,10 +277,10 @@ pub fn on_system_tray_event(event: &str) { tauri::async_runtime::spawn(async move { match ProxiesGuard::global().select_proxy(&group, &name).await { Ok(_) => { - debug!(target: "tray", "select proxy success: {} {}", group, name); + debug!("select proxy success: {} {}", group, name); } Err(e) => { - warn!(target: "tray", "select proxy failed, {} {}, cause: {:?}", group, name, e); + warn!("select proxy failed, {} {}, cause: {:?}", group, name, e); // TODO: add a error dialog or notification } } diff --git a/backend/tauri/src/feat.rs b/backend/tauri/src/feat.rs index 6671c309f9..dc2924725f 100644 --- a/backend/tauri/src/feat.rs +++ b/backend/tauri/src/feat.rs @@ -4,7 +4,12 @@ //! - timer 定时器 //! - cmds 页面调用 //! -use crate::{config::*, core::*, log_err, utils::resolve}; +use crate::{ + config::*, + core::*, + log_err, + utils::{self, resolve}, +}; use anyhow::{bail, Result}; use serde_yaml::{Mapping, Value}; use wry::application::clipboard::Clipboard; @@ -252,6 +257,8 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> { let system_proxy = patch.enable_system_proxy; let proxy_bypass = patch.system_proxy_bypass; let language = patch.language; + let log_level = patch.app_log_level; + let log_max_files = patch.max_log_files; let res = { #[cfg(target_os = "windows")] @@ -296,6 +303,10 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> { handle::Handle::update_systray_part()?; } + if log_level.is_some() || log_max_files.is_some() { + utils::init::refresh_logger((log_level, log_max_files))?; + } + >::Ok(()) }; match res { diff --git a/backend/tauri/src/main.rs b/backend/tauri/src/main.rs index 1e9f50df98..9933bd4c2c 100644 --- a/backend/tauri/src/main.rs +++ b/backend/tauri/src/main.rs @@ -18,7 +18,32 @@ use tauri::{api, SystemTray}; rust_i18n::i18n!("../../locales"); +#[cfg(feature = "deadlock-detection")] +fn deadlock_detection() { + use parking_lot::deadlock; + use std::thread; + use std::time::Duration; + thread::spawn(move || loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = deadlock::check_deadlock(); + if deadlocks.is_empty() { + continue; + } + + println!("{} deadlocks detected", deadlocks.len()); + for (i, threads) in deadlocks.iter().enumerate() { + println!("Deadlock #{}", i); + for t in threads { + println!("Thread Id {:#?}", t.thread_id()); + println!("{:#?}", t.backtrace()); + } + } + }); +} + fn main() -> std::io::Result<()> { + #[cfg(feature = "deadlock-detection")] + deadlock_detection(); // 单例检测 if server::check_singleton().is_err() { println!("app exists"); diff --git a/backend/tauri/src/utils/init/logging.rs b/backend/tauri/src/utils/init/logging.rs new file mode 100644 index 0000000000..3d30951c0b --- /dev/null +++ b/backend/tauri/src/utils/init/logging.rs @@ -0,0 +1,147 @@ +use crate::{config, utils::dirs, Config}; +use anyhow::{anyhow, bail, Result}; +use parking_lot::Mutex; +use std::{ + fs, + io::IsTerminal, + sync::{ + mpsc::{self, Sender}, + OnceLock, + }, + thread, +}; +use tracing::error; +use tracing_appender::{ + non_blocking::{NonBlocking, WorkerGuard}, + rolling::Rotation, +}; +use tracing_subscriber::{filter, fmt, layer::SubscriberExt, reload, EnvFilter}; + +pub type ReloadSignal = (Option, Option); + +struct Channel(Option>); +impl Channel { + fn globals() -> &'static Mutex { + static CHANNEL: OnceLock> = OnceLock::new(); + CHANNEL.get_or_init(|| Mutex::new(Channel(None))) + } +} + +pub fn refresh_logger(signal: ReloadSignal) -> Result<()> { + let channel = Channel::globals().lock(); + match &channel.0 { + Some(sender) => { + let _ = sender.send(signal); + Ok(()) + } + None => bail!("no logger channel"), + } +} + +fn get_file_appender(max_files: usize) -> Result<(NonBlocking, WorkerGuard)> { + let log_dir = dirs::app_logs_dir().unwrap(); + let file_appender = tracing_appender::rolling::Builder::new() + .filename_prefix("clash-nyanpasu") + .filename_suffix("app.log") + .rotation(Rotation::DAILY) + .max_log_files(max_files) + .build(log_dir)?; + Ok(tracing_appender::non_blocking(file_appender)) +} + +/// initial instance global logger +pub fn init() -> Result<()> { + let log_dir = dirs::app_logs_dir().unwrap(); + if !log_dir.exists() { + let _ = fs::create_dir_all(&log_dir); + } + let (log_level, log_max_files) = { + let verge = Config::verge(); + let data = verge.data(); + (data.get_log_level(), data.max_log_files.unwrap_or(7)) + }; + let (filter, filter_handle) = reload::Layer::new( + EnvFilter::builder() + .with_default_directive( + std::convert::Into::::into(log_level).into(), + ) + .from_env_lossy(), + ); + + // register the logger + let (appender, _guard) = get_file_appender(log_max_files)?; + let (file_layer, file_handle) = reload::Layer::new( + fmt::layer() + .json() + .with_writer(appender) + .with_line_number(true) + .with_file(true), + ); + + // spawn a thread to handle the reload signal + thread::spawn(move || { + let mut _guard = _guard; // just hold here to keep the file open + let (sender, receiver) = mpsc::channel::(); + { + let mut channel = Channel::globals().lock(); + channel.0 = Some(sender); + } + loop { + let signal = receiver.recv().unwrap(); + if let Some(level) = signal.0 { + filter_handle + .reload( + EnvFilter::builder() + .with_default_directive( + std::convert::Into::::into(level).into(), + ) + .from_env_lossy(), + ) + .unwrap(); // panic if error + } + + if let Some(max_files) = signal.1 { + let (appender, guard) = match get_file_appender(max_files) { + Ok(x) => x, + Err(e) => { + error!("failed to create file appender: {}", e); + continue; + } + }; + _guard = guard; + if let Err(e) = file_handle.modify(|layer| *layer.writer_mut() = appender) { + error!("failed to modify file appender: {}", e); + } + } + } + }); + + // if debug build, log to stdout and stderr with all levels + let terminal_layer = { + #[cfg(debug_assertions)] + { + Some( + fmt::Layer::new() + .with_ansi(std::io::stdout().is_terminal()) + .compact() + .with_target(false) + .with_file(true) + .with_line_number(true) + .with_writer(std::io::stdout), + ) + } + #[cfg(not(debug_assertions))] + { + None + } + }; + + let subscriber = tracing_subscriber::registry() + .with(filter) + .with(file_layer) + .with(terminal_layer); + + tracing::subscriber::set_global_default(subscriber) + .map_err(|x| anyhow!("setup logging error: {}", x))?; + Ok(()) +} diff --git a/backend/tauri/src/utils/init.rs b/backend/tauri/src/utils/init/mod.rs similarity index 75% rename from backend/tauri/src/utils/init.rs rename to backend/tauri/src/utils/init/mod.rs index f350ea54fe..fdff66dd28 100644 --- a/backend/tauri/src/utils/init.rs +++ b/backend/tauri/src/utils/init/mod.rs @@ -3,75 +3,11 @@ use crate::{ utils::{dialog::migrate_dialog, dirs, help}, }; use anyhow::Result; -use chrono::Local; -use log::LevelFilter; -use log4rs::{ - append::{console::ConsoleAppender, file::FileAppender}, - config::{Appender, Logger, Root}, - encode::pattern::PatternEncoder, -}; use runas::Command as RunasCommand; use std::{fs, io::ErrorKind, path::PathBuf}; -/// initialize this instance's log file -fn init_log() -> Result<()> { - let log_dir = dirs::app_logs_dir()?; - if !log_dir.exists() { - let _ = fs::create_dir_all(&log_dir); - } - - let log_level = Config::verge().data().get_log_level(); - if log_level == LevelFilter::Off { - return Ok(()); - } - - let local_time = Local::now().format("%Y-%m-%d-%H%M").to_string(); - let log_file = format!("{}.log", local_time); - let log_file = log_dir.join(log_file); - - let log_pattern = match log_level { - LevelFilter::Trace => "{d(%Y-%m-%d %H:%M:%S)} {l} [{M}] - {m}{n}", - _ => "{d(%Y-%m-%d %H:%M:%S)} {l} - {m}{n}", - }; - - let encode = Box::new(PatternEncoder::new(log_pattern)); - - let stdout = ConsoleAppender::builder().encoder(encode.clone()).build(); - let tofile = FileAppender::builder().encoder(encode).build(log_file)?; - - let mut logger_builder = Logger::builder(); - let mut root_builder = Root::builder(); - - let log_more = log_level == LevelFilter::Trace || log_level == LevelFilter::Debug; - - #[cfg(feature = "verge-dev")] - { - logger_builder = logger_builder.appenders(["file", "stdout"]); - if log_more { - root_builder = root_builder.appenders(["file", "stdout"]); - } else { - root_builder = root_builder.appenders(["stdout"]); - } - } - #[cfg(not(feature = "verge-dev"))] - { - logger_builder = logger_builder.appenders(["file"]); - if log_more { - root_builder = root_builder.appenders(["file"]); - } - } - - let (config, _) = log4rs::config::Config::builder() - .appender(Appender::builder().build("stdout", Box::new(stdout))) - .appender(Appender::builder().build("file", Box::new(tofile))) - .logger(logger_builder.additive(false).build("app", log_level)) - .build_lossy(root_builder.build(log_level)); - - log4rs::init_config(config)?; - - Ok(()) -} - +mod logging; +pub use logging::refresh_logger; /// Initialize all the config files /// before tauri setup pub fn init_config() -> Result<()> { @@ -101,7 +37,7 @@ pub fn init_config() -> Result<()> { } // init log - let _ = init_log(); + logging::init().unwrap(); crate::log_err!(dirs::app_profiles_dir().map(|profiles_dir| { if !profiles_dir.exists() { diff --git a/package.json b/package.json index f33173ea52..c09e17d9df 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "license": "GPL-3.0", "type": "module", "scripts": { - "dev": "tauri dev -f default-meta -c ./backend/tauri/tauri.conf.json", - "dev:diff": "tauri dev -f verge-dev -c ./backend/tauri/tauri.conf.json", - "build": "tauri build -f default-meta -c ./backend/tauri/tauri.conf.json", - "build:nightly": "tauri build -f default-meta nightly -c ./backend/tauri/tauri.nightly.conf.json", + "dev": "tauri dev -c ./backend/tauri/tauri.conf.json", + "dev:diff": "tauri dev -f verge-dev deadlock-detection -c ./backend/tauri/tauri.conf.json", + "build": "tauri build -c ./backend/tauri/tauri.conf.json", + "build:nightly": "tauri build -f deadlock-detection nightly -c ./backend/tauri/tauri.nightly.conf.json", "tauri": "tauri", "web:dev": "vite", "web:build": "vite build", diff --git a/src/components/setting/mods/tasks-viewer.tsx b/src/components/setting/mods/tasks-viewer.tsx index d959bfc769..ff8941aa5b 100644 --- a/src/components/setting/mods/tasks-viewer.tsx +++ b/src/components/setting/mods/tasks-viewer.tsx @@ -1,7 +1,7 @@ import { BaseDialog, DialogRef } from "@/components/base"; import { NotificationType, useNotification } from "@/hooks/use-notification"; import { useVerge } from "@/hooks/use-verge"; -import { List, ListItem, ListItemText, MenuItem, Select } from "@mui/material"; +import { List, ListItem, ListItemText, TextField } from "@mui/material"; import { useLockFn } from "ahooks"; import { forwardRef, useImperativeHandle, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -13,14 +13,14 @@ export const TasksViewer = forwardRef( const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const [values, setValues] = useState({ - auto_log_clean: 0, + max_log_files: 0, }); useImperativeHandle(ref, () => ({ open: () => { setOpen(true); setValues({ - auto_log_clean: verge?.auto_log_clean ?? 0, + max_log_files: verge?.max_log_files ?? 7, }); }, close: () => setOpen(false), @@ -29,7 +29,7 @@ export const TasksViewer = forwardRef( setLoading(true); try { await patchVerge({ - auto_log_clean: values.auto_log_clean, + max_log_files: values.max_log_files, }); setOpen(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -58,30 +58,19 @@ export const TasksViewer = forwardRef( > - - + /> diff --git a/src/locales/en.json b/src/locales/en.json index 7de427cee3..2e4d20871a 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -113,6 +113,8 @@ "Retain 7 Days": "Retain 7 Days", "Retain 30 Days": "Retain 30 Days", "Retain 90 Days": "Retain 90 Days", + "Max Log Files": "Max Log Files", + "Collect Logs": "Collect Logs", "Back": "Back", "Save": "Save", diff --git a/src/locales/ru.json b/src/locales/ru.json index 93d48715c0..6bcba59931 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -109,6 +109,7 @@ "Retain 7 Days": "Сохранять 7 дней", "Retain 30 Days": "Сохранять 30 дней", "Retain 90 Days": "Сохранять 90 дней", + "Max Log Files": "Максимальное количество файлов логов", "Collect Logs": "Собрать логи", diff --git a/src/locales/zh.json b/src/locales/zh.json index b5b445ca6c..4a6ffcda39 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -120,9 +120,9 @@ "Retain 7 Days": "保留7天", "Retain 30 Days": "保留30天", "Retain 90 Days": "保留90天", + "Max Log Files": "最大日志文件数", "Collect Logs": "收集日志", - "Back": "返回", "Save": "保存", "Cancel": "取消", diff --git a/src/services/types.d.ts b/src/services/types.d.ts index aac91e79be..001f0037c2 100644 --- a/src/services/types.d.ts +++ b/src/services/types.d.ts @@ -186,7 +186,7 @@ interface IVergeConfig { css_injection?: string; page_transition_duration?: number; }; - auto_log_clean?: number; + max_log_files?: number; auto_close_connection?: boolean; default_latency_test?: string; enable_clash_fields?: boolean;