From fbeb0e56beb06f01c576ac6d2e7d0dc6b20c1f5c Mon Sep 17 00:00:00 2001 From: Shreyas Date: Fri, 25 Apr 2025 17:09:51 +0530 Subject: [PATCH] feat(desktop): file-based logs with rotation (#5009) This PR adds a file-based logging system with size-based rotation to the desktop application. It essentially redirects existing diagnostic to size-based rotating files for troubleshooting environment-specific issues. Closes HFE-801 The desktop application currently lacks a persistent logging mechanism in production environments. Logs are only available through the development mode console. This PR will help diagnose issues reported in #4859, #4950, #5003, discussions #4984 and #4986. Mainly aiming to understand errors in specific environments that can't be reproduced in our testing setups. This implementation uses the tracing ecosystem (`tracing`, `tracing_subscriber`, `tracing_appender`) along with `file_rotate` to create log files in the platform's log directory. The logs are automatically rotated when they reach `10MB`, with a maximum of `5` files retained. Thinking 10 * 5 MB is reasonable disk usage while maintaining sufficient history. The system currently writes to both the console (with ANSI colors where supported) and to files (without ANSI formatting for readability). Log levels are currently controlled via the `RUST_LOG` environment variable, defaulting to "debug" when not specified. | OS | Log File Path | |---------|------------------------------------------------------| | Windows | `C:\Users\\AppData\Local\io.hoppscotch.desktop\logs\io.hoppscotch.desktop.log` | | macOS | `~/Library/Logs/io.hoppscotch.desktop/io.hoppscotch.desktop.log` | | Linux | `~/.local/share/io.hoppscotch.desktop/logs/io.hoppscotch.desktop.log` | --- .../hoppscotch-desktop/src-tauri/Cargo.lock | 132 ++++++------------ .../hoppscotch-desktop/src-tauri/Cargo.toml | 2 + .../hoppscotch-desktop/src-tauri/src/lib.rs | 17 ++- .../src-tauri/src/logger.rs | 52 +++++++ .../hoppscotch-desktop/src-tauri/src/main.rs | 13 +- 5 files changed, 111 insertions(+), 105 deletions(-) create mode 100644 packages/hoppscotch-desktop/src-tauri/src/logger.rs diff --git a/packages/hoppscotch-desktop/src-tauri/Cargo.lock b/packages/hoppscotch-desktop/src-tauri/Cargo.lock index e8051982..b204023a 100644 --- a/packages/hoppscotch-desktop/src-tauri/Cargo.lock +++ b/packages/hoppscotch-desktop/src-tauri/Cargo.lock @@ -1444,6 +1444,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "file-rotate" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8e2fa049328a1f3295991407a88585805d126dfaadf74b9fe8c194c730aafc" +dependencies = [ + "chrono", + "flate2", +] + [[package]] name = "filetime" version = "0.2.25" @@ -2026,6 +2036,7 @@ name = "hoppscotch-desktop" version = "25.3.2" dependencies = [ "axum", + "file-rotate", "portpicker", "serde", "serde_json", @@ -2044,6 +2055,7 @@ dependencies = [ "tokio", "tower-http", "tracing", + "tracing-appender", "tracing-subscriber", ] @@ -2619,9 +2631,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libloading" @@ -2928,15 +2940,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3063,15 +3066,6 @@ dependencies = [ "objc2-foundation", ] -[[package]] -name = "objc2-core-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "objc2-core-image" version = "0.2.2" @@ -4747,16 +4741,13 @@ dependencies = [ ] [[package]] -name = "sysinfo" -version = "0.34.2" +name = "sys-info" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b93974b3d3aeaa036504b8eefd4c039dced109171c1ae973f1dc63b2c7e4b2" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" dependencies = [ + "cc", "libc", - "memchr", - "ntapi", - "objc2-core-foundation", - "windows 0.57.0", ] [[package]] @@ -4993,7 +4984,7 @@ dependencies = [ [[package]] name = "tauri-plugin-appload" version = "0.1.0" -source = "git+https://github.com/CuriousCorrelation/tauri-plugin-appload#60adb82d0cc886004307057194cf680373e14e02" +source = "git+https://github.com/CuriousCorrelation/tauri-plugin-appload#1c2e8b19db7f1b6af6d00abb907f15cdc2017298" dependencies = [ "base64 0.22.1", "blake3", @@ -5016,7 +5007,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sysinfo", + "sys-info", "tauri", "tauri-plugin", "thiserror 2.0.7", @@ -5045,7 +5036,7 @@ dependencies = [ "tracing", "url", "windows-registry 0.3.0", - "windows-result 0.2.0", + "windows-result", ] [[package]] @@ -5606,6 +5597,18 @@ 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", + "thiserror 1.0.69", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.28" @@ -6148,8 +6151,8 @@ dependencies = [ "webview2-com-sys", "windows 0.58.0", "windows-core 0.58.0", - "windows-implement 0.58.0", - "windows-interface 0.58.0", + "windows-implement", + "windows-interface", ] [[package]] @@ -6228,16 +6231,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -6257,42 +6250,19 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", + "windows-implement", + "windows-interface", + "windows-result", "windows-strings 0.1.0", "windows-targets 0.52.6", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "windows-implement" version = "0.58.0" @@ -6304,17 +6274,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "windows-interface" version = "0.58.0" @@ -6332,7 +6291,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -6343,20 +6302,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafa604f2104cf5ae2cc2db1dee84b7e6a5d11b05f737b60def0ffdc398cbc0a" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-strings 0.2.0", "windows-targets 0.52.6", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.2.0" @@ -6372,7 +6322,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-targets 0.52.6", ] diff --git a/packages/hoppscotch-desktop/src-tauri/Cargo.toml b/packages/hoppscotch-desktop/src-tauri/Cargo.toml index f8416ea3..c17afe6b 100644 --- a/packages/hoppscotch-desktop/src-tauri/Cargo.toml +++ b/packages/hoppscotch-desktop/src-tauri/Cargo.toml @@ -24,6 +24,7 @@ serde = { version = "1", features = ["derive"] } serde_json = { version = "1", features = [] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +tracing-appender = { version = "0.2.3" } tauri-plugin-store = "2.2.0" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" @@ -35,6 +36,7 @@ tower-http = { version = "0.6.2", features = ["cors"] } portpicker = "0.1.1" tokio = "1.43.0" tauri-plugin-process = "2.2.0" +file-rotate = "0.8.0" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-updater = "2.3.1" diff --git a/packages/hoppscotch-desktop/src-tauri/src/lib.rs b/packages/hoppscotch-desktop/src-tauri/src/lib.rs index a8c48a19..c7956a57 100644 --- a/packages/hoppscotch-desktop/src-tauri/src/lib.rs +++ b/packages/hoppscotch-desktop/src-tauri/src/lib.rs @@ -1,3 +1,4 @@ +pub mod logger; pub mod server; pub mod updater; @@ -19,6 +20,12 @@ fn hopp_auth_port() -> u16 { pub fn run() { tracing::info!("Starting Hoppscotch Desktop v{}", env!("CARGO_PKG_VERSION")); + let server_port = portpicker::pick_unused_port().expect("Cannot find unused port"); + SERVER_PORT + .set(server_port) + .expect("Failed to set server port"); + tracing::info!("Selected server port: {}", server_port); + tauri::Builder::default() .plugin( tauri_plugin_window_state::Builder::new() @@ -61,14 +68,20 @@ pub fn run() { }) .setup(|app| { let handle = app.handle().clone(); - let port = portpicker::pick_unused_port().expect("Cannot find unused port"); - SERVER_PORT.set(port).expect("Failed to set server port"); + + let port = *SERVER_PORT.get().expect("Server port not initialized"); + tracing::info!(port = port, "Initializing server with pre-selected port"); let port = server::init(port, handle); tracing::info!(port = port, "Server initialization complete"); Ok(()) }) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_fs::init()) + .setup(|app| { + logger::setup(app.handle().clone())?; + tracing::info!("Logger setup complete"); + Ok(()) + }) .plugin(tauri_plugin_appload::init( VendorConfigBuilder::new().bundle( include_bytes!("../../bundle.zip").to_vec(), diff --git a/packages/hoppscotch-desktop/src-tauri/src/logger.rs b/packages/hoppscotch-desktop/src-tauri/src/logger.rs new file mode 100644 index 00000000..afc94234 --- /dev/null +++ b/packages/hoppscotch-desktop/src-tauri/src/logger.rs @@ -0,0 +1,52 @@ +use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate}; +use tauri::{AppHandle, Manager, Runtime}; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; + +pub struct LogGuard(pub tracing_appender::non_blocking::WorkerGuard); + +pub fn setup(app_handle: AppHandle) -> Result<(), Box> { + let log_dir = app_handle.path().app_log_dir()?; + std::fs::create_dir_all(&log_dir)?; + + let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| format!("debug").into()); + + let log_file_path = log_dir.join("io.hoppscotch.desktop.log"); + tracing::info!(log_file_path =? &log_file_path); + + let file = FileRotate::new( + &log_file_path, + AppendCount::new(5), + ContentLimit::Bytes(10 * 1024 * 1024), + Compression::None, + None, + ); + + let (non_blocking, guard) = tracing_appender::non_blocking(file); + + let console_layer = fmt::layer() + .with_writer(std::io::stdout) + .with_thread_ids(true) + .with_thread_names(true) + .with_ansi(!cfg!(target_os = "windows")); + + let file_layer = fmt::layer() + .with_writer(non_blocking) + .with_ansi(false) + .with_thread_ids(true) + .with_thread_names(true); + + tracing_subscriber::registry() + .with(env_filter) + .with(file_layer) + .with(console_layer) + .init(); + + app_handle.manage(LogGuard(guard)); + + tracing::info!( + log_file = %log_file_path.display(), + "Logging initialized with single file" + ); + + Ok(()) +} diff --git a/packages/hoppscotch-desktop/src-tauri/src/main.rs b/packages/hoppscotch-desktop/src-tauri/src/main.rs index e55176ac..aeae0164 100644 --- a/packages/hoppscotch-desktop/src-tauri/src/main.rs +++ b/packages/hoppscotch-desktop/src-tauri/src/main.rs @@ -1,18 +1,7 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; - fn main() { - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()), - ) - .with(tracing_subscriber::fmt::layer().without_time()) - .init(); - - tracing::info!("Starting Hoppscotch Desktop..."); - + println!("Starting Hoppscotch Desktop application..."); hoppscotch_desktop_lib::run() }