feat(agent): file-based logs with rotation (#5147)
This commit is contained in:
parent
4302a45477
commit
9ef2c461ba
8 changed files with 122 additions and 64 deletions
14
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
14
packages/hoppscotch-agent/src-tauri/Cargo.lock
generated
|
|
@ -1476,6 +1476,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"
|
||||
|
|
@ -2074,6 +2084,8 @@ dependencies = [
|
|||
"base16",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"dirs 6.0.0",
|
||||
"file-rotate",
|
||||
"lazy_static",
|
||||
"native-dialog",
|
||||
"rand 0.8.5",
|
||||
|
|
@ -6208,7 +6220,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ tauri-plugin-single-instance = "2.0.1"
|
|||
tauri-plugin-http = { version = "2.0.1", features = ["gzip"] }
|
||||
native-dialog = "0.7.0"
|
||||
sha2 = "0.10.8"
|
||||
file-rotate = "0.8.0"
|
||||
dirs = "6.0.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
tempfile = { version = "3.13.0" }
|
||||
|
|
|
|||
|
|
@ -53,11 +53,17 @@ pub enum AgentError {
|
|||
#[error("IO error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("Log init error: {0}")]
|
||||
LogInit(#[from] tracing_appender::rolling::InitError),
|
||||
LogInit(String),
|
||||
#[error("Log init global error: {0}")]
|
||||
LogInitGlobal(#[from] tracing::subscriber::SetGlobalDefaultError),
|
||||
}
|
||||
|
||||
impl From<tracing_appender::rolling::InitError> for AgentError {
|
||||
fn from(err: tracing_appender::rolling::InitError) -> Self {
|
||||
AgentError::LogInit(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for AgentError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, error_message) = match self {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ pub mod controller;
|
|||
pub mod dialog;
|
||||
pub mod error;
|
||||
pub mod global;
|
||||
pub mod logger;
|
||||
pub mod model;
|
||||
pub mod route;
|
||||
pub mod server;
|
||||
|
|
@ -16,12 +17,13 @@ use std::sync::Arc;
|
|||
use tauri::{AppHandle, Emitter, Listener, Manager, WebviewWindowBuilder};
|
||||
use tauri_plugin_updater::UpdaterExt;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing_subscriber::{fmt::format::JsonFields, EnvFilter};
|
||||
|
||||
use error::{AgentError, AgentResult};
|
||||
use model::{LogGuard, Payload};
|
||||
use model::Payload;
|
||||
use state::AppState;
|
||||
|
||||
pub const HOPPSCOTCH_AGENT_IDENTIFIER: &str = "io.hoppscotch.agent";
|
||||
|
||||
#[tracing::instrument(skip(app_handle))]
|
||||
fn create_main_window(app_handle: &AppHandle) -> AgentResult<()> {
|
||||
tracing::info!("Creating main application window");
|
||||
|
|
@ -100,8 +102,6 @@ pub fn run() {
|
|||
}))
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.setup(move |app| {
|
||||
// let _ = setup_logging(&app.handle())?;
|
||||
|
||||
tracing::info!("Setting up application");
|
||||
let app_handle = app.handle();
|
||||
|
||||
|
|
@ -258,47 +258,3 @@ pub fn run() {
|
|||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(app_handle))]
|
||||
pub fn setup_logging(app_handle: &AppHandle) -> AgentResult<()> {
|
||||
tracing::info!("Setting up logging system");
|
||||
|
||||
let app_data_dir = app_handle.path().app_data_dir()?;
|
||||
tracing::debug!(path = ?app_data_dir, "Creating app data directory");
|
||||
std::fs::create_dir_all(&app_data_dir)?;
|
||||
|
||||
tracing::debug!("Configuring file appender");
|
||||
let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
|
||||
.rotation(tracing_appender::rolling::Rotation::DAILY)
|
||||
.filename_prefix("hoppscotch-agent")
|
||||
.filename_suffix("log")
|
||||
.max_log_files(1)
|
||||
.build(&app_data_dir)?;
|
||||
|
||||
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
tracing::debug!("Building subscriber with JSON formatting");
|
||||
let subscriber = tracing_subscriber::fmt()
|
||||
.fmt_fields(JsonFields::new())
|
||||
.with_target(false)
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(false)
|
||||
.with_timer(tracing_subscriber::fmt::time::UtcTime::rfc_3339())
|
||||
.with_env_filter(EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
if cfg!(debug_assertions) {
|
||||
"debug"
|
||||
} else {
|
||||
"info"
|
||||
}
|
||||
.into()
|
||||
}))
|
||||
.with_filter_reloading()
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
app_handle.manage(LogGuard(_guard));
|
||||
|
||||
tracing::info!("Logging system initialized successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
53
packages/hoppscotch-agent/src-tauri/src/logger.rs
Normal file
53
packages/hoppscotch-agent/src-tauri/src/logger.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
|
||||
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
||||
use crate::HOPPSCOTCH_AGENT_IDENTIFIER;
|
||||
|
||||
pub struct LogGuard(pub tracing_appender::non_blocking::WorkerGuard);
|
||||
|
||||
pub fn setup(log_dir: &PathBuf) -> Result<LogGuard, Box<dyn std::error::Error>> {
|
||||
std::fs::create_dir_all(log_dir)?;
|
||||
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| "debug".into());
|
||||
|
||||
let log_file_path = log_dir.join(&format!("{}.log", HOPPSCOTCH_AGENT_IDENTIFIER));
|
||||
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)
|
||||
.with_timer(tracing_subscriber::fmt::time::UtcTime::rfc_3339());
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(file_layer)
|
||||
.with(console_layer)
|
||||
.init();
|
||||
|
||||
tracing::info!(
|
||||
log_file = %log_file_path.display(),
|
||||
"Logging initialized with rotating file"
|
||||
);
|
||||
|
||||
Ok(LogGuard(guard))
|
||||
}
|
||||
|
|
@ -1,16 +1,46 @@
|
|||
// 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};
|
||||
use hoppscotch_agent_lib::{
|
||||
logger::{self, LogGuard},
|
||||
HOPPSCOTCH_AGENT_IDENTIFIER,
|
||||
};
|
||||
|
||||
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();
|
||||
// Follows how `tauri` does this and exactly matches desktop's approach
|
||||
// see: https://github.com/tauri-apps/tauri/blob/dev/crates/tauri/src/path/desktop.rs
|
||||
let path = {
|
||||
#[cfg(target_os = "macos")]
|
||||
let path =
|
||||
dirs::home_dir().map(|dir| dir.join("Library/Logs").join(HOPPSCOTCH_AGENT_IDENTIFIER));
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
let path =
|
||||
dirs::data_local_dir().map(|dir| dir.join(HOPPSCOTCH_AGENT_IDENTIFIER).join("logs"));
|
||||
|
||||
path
|
||||
};
|
||||
|
||||
let Some(log_file_path) = path else {
|
||||
eprint!("Failed to setup logging!");
|
||||
|
||||
println!("Starting Hoppscotch Agent...");
|
||||
|
||||
return hoppscotch_agent_lib::run();
|
||||
};
|
||||
|
||||
let Ok(LogGuard(guard)) = logger::setup(&log_file_path) else {
|
||||
eprint!("Failed to setup logging!");
|
||||
|
||||
println!("Starting Hoppscotch Agent...");
|
||||
|
||||
return hoppscotch_agent_lib::run();
|
||||
};
|
||||
|
||||
// This keeps the guard alive, this is scoped to `main`
|
||||
// so it can only drop when the entire app exits,
|
||||
// so safe to have it like this.
|
||||
let _guard = guard;
|
||||
|
||||
tracing::info!("Starting Hoppscotch Agent...");
|
||||
|
||||
|
|
|
|||
|
|
@ -96,10 +96,9 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> {
|
|||
}
|
||||
}
|
||||
"maximize_window" => {
|
||||
app.emit("maximize-window", ())
|
||||
.unwrap_or_else(|e| {
|
||||
tracing::error!("Failed to emit maximize-window event: {}", e);
|
||||
});
|
||||
app.emit("maximize-window", ()).unwrap_or_else(|e| {
|
||||
tracing::error!("Failed to emit maximize-window event: {}", e);
|
||||
});
|
||||
if let Err(e) = show_main_window(&app) {
|
||||
tracing::error!("Failed to maximize window: {}", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub struct LogGuard(pub tracing_appender::non_blocking::WorkerGuard);
|
|||
pub fn setup(log_dir: &PathBuf) -> Result<LogGuard, Box<dyn std::error::Error>> {
|
||||
std::fs::create_dir_all(log_dir)?;
|
||||
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| format!("debug").into());
|
||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| "debug".into());
|
||||
|
||||
let log_file_path = log_dir.join(&format!("{}.log", HOPPSCOTCH_DESKTOP_IDENTIFIER));
|
||||
tracing::info!(log_file_path =? &log_file_path);
|
||||
|
|
@ -45,7 +45,7 @@ pub fn setup(log_dir: &PathBuf) -> Result<LogGuard, Box<dyn std::error::Error>>
|
|||
|
||||
tracing::info!(
|
||||
log_file = %log_file_path.display(),
|
||||
"Logging initialized with single file"
|
||||
"Logging initialized with rotating file"
|
||||
);
|
||||
|
||||
Ok(LogGuard(guard))
|
||||
|
|
|
|||
Loading…
Reference in a new issue