diff --git a/Cargo.toml b/Cargo.toml index 6ea1ae6..68809bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ textwrap = "0.16.0" ansi_colours = "1.2.2" eventsource-stream = "0.2.3" log = "0.4.28" -log4rs = { version = "1.4.0", features = ["file_appender"] } +log4rs = { version = "1.4.0", features = ["file_appender", "rolling_file_appender", "compound_policy", "fixed_window_roller", "size_trigger"] } shell-words = "1.1.0" sha2 = "0.10.8" unicode-width = "0.2.0" diff --git a/src/main.rs b/src/main.rs index 218bdb2..d97eda1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,10 @@ use client::ClientConfig; use inquire::{Select, Text, set_global_render_config}; use log::{LevelFilter, warn}; use log4rs::append::console::ConsoleAppender; -use log4rs::append::file::FileAppender; +use log4rs::append::rolling_file::RollingFileAppender; +use log4rs::append::rolling_file::policy::compound::CompoundPolicy; +use log4rs::append::rolling_file::policy::compound::roll::fixed_window::FixedWindowRoller; +use log4rs::append::rolling_file::policy::compound::trigger::size::SizeTrigger; use log4rs::config::{Appender, Logger, Root}; use log4rs::encode::pattern::PatternEncoder; use oauth::OAuthProvider; @@ -585,7 +588,20 @@ fn setup_logger() -> Result> { } Some(path) => { ensure_parent_exists(&path)?; - let file_appender = FileAppender::builder().encoder(encoder.clone()).build(path); + + let archive_pattern = path + .with_extension("archived.{}.log") + .to_string_lossy() + .into_owned(); + let trigger = SizeTrigger::new(10 * 1024 * 1024); + let roller = FixedWindowRoller::builder() + .build(&archive_pattern, 5) + .unwrap(); + let policy = CompoundPolicy::new(Box::new(trigger), Box::new(roller)); + + let file_appender = RollingFileAppender::builder() + .encoder(encoder.clone()) + .build(path, Box::new(policy)); match file_appender { Ok(appender) => { @@ -608,7 +624,7 @@ fn setup_logger() -> Result> { fn init_file_logger( log_level: LevelFilter, log_filter: Option, - file_appender: FileAppender, + file_appender: RollingFileAppender, ) -> log4rs::Config { let root_log_level = if log_filter.is_some() { LevelFilter::Off diff --git a/src/utils/logs.rs b/src/utils/logs.rs index 58c8089..b7da422 100644 --- a/src/utils/logs.rs +++ b/src/utils/logs.rs @@ -1,9 +1,11 @@ use crate::config::paths; use colored::Colorize; use fancy_regex::Regex; -use std::fs::File; +use std::fs::{self, File}; use std::io::{BufRead, BufReader, Seek, SeekFrom}; use std::process; +use std::time::Duration; +use tokio::time::sleep; pub async fn tail_logs(no_color: bool) { let re = Regex::new(r"^(?P\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s+<(?P[^\s>]+)>\s+\[(?P[A-Z]+)\]\s+(?P[^:]+):(?P\d+)\s+-\s+(?P.*)$").unwrap(); @@ -16,20 +18,43 @@ pub async fn tail_logs(no_color: bool) { process::exit(1); }; - let mut lines = reader.lines(); + let mut line_buf = String::new(); loop { - if let Some(Ok(line)) = lines.next() { - if no_color { - println!("{line}"); - } else { - let colored_line = colorize_log_line(&line, &re); - println!("{colored_line}"); + match reader.read_line(&mut line_buf) { + Ok(0) => { + if file_was_rotated(&file_path, &mut reader) { + let file = File::open(&file_path).expect("Cannot open file"); + reader = BufReader::new(file); + } + sleep(Duration::from_millis(100)).await; + } + Ok(_) => { + let line = line_buf.trim_end(); + if no_color { + println!("{line}"); + } else { + let colored_line = colorize_log_line(line, &re); + println!("{colored_line}"); + } + line_buf.clear(); + } + Err(_) => { + line_buf.clear(); + sleep(Duration::from_millis(100)).await; } } } } +fn file_was_rotated(path: &std::path::Path, reader: &mut BufReader) -> bool { + let current_pos = reader.stream_position().unwrap_or(0); + match fs::metadata(path) { + Ok(metadata) => metadata.len() < current_pos, + Err(_) => true, + } +} + fn colorize_log_line(line: &str, re: &Regex) -> String { if let Some(caps) = re.captures(line).expect("Failed to capture log line") { let level = &caps["level"];