From f9caed33381db0cfffa24108d6ebd4f32592e4aa Mon Sep 17 00:00:00 2001 From: samwuel Date: Fri, 10 Apr 2026 18:13:25 +0300 Subject: [PATCH] logger enhancement --- src/config.rs | 6 +++- src/logger.rs | 87 ++++++++++++++++++++++++++++++++++----------------- src/main.rs | 54 +++++++++++++------------------- 3 files changed, 85 insertions(+), 62 deletions(-) diff --git a/src/config.rs b/src/config.rs index 28673a7..83be97b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -159,6 +159,7 @@ pub struct Config { pub cache_ttl: u16, pub threads: u16, pub log_file: Option, + pub log_level: Option, pub index: String, // Index file to serve by default // pub error: String, // Error file to serve when a file is not found pub proxy_rules: HashMap, @@ -185,6 +186,7 @@ impl Config { index: "index.html".to_string(), log_file: None, //error: "error.html".to_string(), + log_level: None, threads: 1, cache: false, cache_ttl: 0, @@ -213,7 +215,7 @@ impl Config { root: root_dir, index: file_name.to_string(), log_file: None, - + log_level: None, threads: 1, cache: false, cache_ttl: 0, @@ -278,6 +280,7 @@ impl Config { index: map.get2("index").unwrap_or("index.html".to_string()), log_file: map.get2("log_file"), //error: map.get2("error").unwrap_or("error.html".to_string()), + log_level: map.get2("log_level"), proxy_rules, } } @@ -293,6 +296,7 @@ impl Config { cache_ttl: 0, threads: 2, log_file: None, + log_level: None, index: "index.html".to_string(), proxy_rules, } diff --git a/src/logger.rs b/src/logger.rs index 276a11c..7a77122 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,7 +1,7 @@ use std::fmt; use std::io::Write; use std::sync::Arc; -use std::sync::mpsc::{Sender, channel}; +use std::sync::mpsc::{SyncSender, sync_channel}; use std::thread::{self, JoinHandle}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -114,15 +114,14 @@ impl SimpleTime { /// Internal message structure containing log metadata. struct LogMessage { - timestamp: String, level: LogLevel, - component: String, + component: Arc, content: String, } /// Core logger implementation running in a background thread. struct LoggerCore { - tx: Sender, + tx: SyncSender, _thread: JoinHandle<()>, } @@ -143,41 +142,37 @@ impl Logger { min_level: LogLevel, component: &str, ) -> Logger { - let (tx, rx) = channel::(); + let (tx, rx) = sync_channel::(4096); let thread = thread::spawn(move || { let mut last_flush = Instant::now(); - let mut buff = Vec::new(); - let mut max_size = 100; + let mut buff = String::with_capacity(8196); + let mut max_size = 100usize; let timeout = Duration::from_secs(1); loop { let msg = rx.recv_timeout(timeout); match msg { Ok(msg) => { - let formatted = format!( + let ts = SimpleTime::get_current_timestamp(); + buff.push_str(&format!( "{} [{}] [{}] {}\n", - msg.timestamp, msg.level, msg.component, msg.content - ); - buff.push(formatted); + ts, msg.level, msg.component, msg.content + )); } Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {} Err(_) => break, } // Flush if timeout or buffer threshold reached - if last_flush.elapsed() >= timeout || buff.len() >= max_size { + if last_flush.elapsed() >= timeout || buff.len() >= max_size * 80 { if !buff.is_empty() { - if buff.len() >= max_size { - max_size = (max_size * 10).min(1_000_000); + max_size = if buff.len() >= max_size * 80 { + (max_size * 10).min(1_000_000) } else { - max_size = (max_size / 10).max(100); - } - let wr = writer.write_all(buff.join("").as_bytes()); - if wr.is_err() { - println!("Failed to write to log: {:?}", wr); - } + (max_size / 10).max(100) + }; + let _ = writer.write_all(buff.as_bytes()); let _ = writer.flush(); - buff.clear(); } last_flush = Instant::now(); @@ -204,21 +199,22 @@ impl Logger { } } + #[inline] + pub fn enabled(&self, level:LogLevel) -> bool { + level >= self.min_level + } + /// Sends a log message with the given level and content. pub fn log(&self, level: LogLevel, content: String) { - if level < self.min_level { + if !self.enabled(level) { return; } - let log_msg = LogMessage { - timestamp: SimpleTime::get_current_timestamp(), + let _ = self.core.tx.try_send(LogMessage { level, - component: (*self.component).clone(), + component: Arc::clone(&self.component), content, - }; - - // Send the log message to the channel - let _ = self.core.tx.send(log_msg); + }); } /// Logs a DEBUG-level message. @@ -263,6 +259,39 @@ impl Clone for Logger { } } +#[macro_export] +macro_rules! log_debug { + ($l:expr, $($a:tt)*) => { + if $l.enabled($crate::logger::LogLevel::DEBUG) { + $l.log($crate::logger::LogLevel::DEBUG, format!($($a)*)); + } + }; +} +#[macro_export] +macro_rules! log_info { + ($l:expr, $($a:tt)*) => { + if $l.enabled($crate::logger::LogLevel::INFO) { + $l.log($crate::logger::LogLevel::INFO, format!($($a)*)); + } + }; +} +#[macro_export] +macro_rules! log_warn { + ($l:expr, $($a:tt)*) => { + if $l.enabled($crate::logger::LogLevel::WARN) { + $l.log($crate::logger::LogLevel::WARN, format!($($a)*)); + } + }; +} +#[macro_export] +macro_rules! log_error { + ($l:expr, $($a:tt)*) => { + if $l.enabled($crate::logger::LogLevel::ERROR) { + $l.log($crate::logger::LogLevel::ERROR, format!($($a)*)); + } + }; +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index d088a01..ab5ccf8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,10 +119,13 @@ fn main() { // Determine if the server should proxy all requests let proxy_only = config.proxy_rules.get("/").is_some(); - let min_log = if cfg!(debug_assertions) { - LogLevel::DEBUG - } else { - LogLevel::INFO + let min_log = match config.log_level.as_deref() { + Some("debug") => LogLevel::DEBUG, + Some("info") => LogLevel::INFO, + Some("warn") => LogLevel::WARN, + Some("error") => LogLevel::ERROR, + Some("none") => LogLevel::FATAL, + _ => if cfg!(debug_assertions) { LogLevel::DEBUG } else { LogLevel::INFO }, }; // Initialize the logger based on the config or default to stdout if the log file can't be created let logger = match config.log_file.clone() { @@ -152,20 +155,16 @@ fn main() { //Configure graceful shutdown from ctrl+c shutdown::setup_graceful_shutdown(&mut server, logger.clone()); - logger.info(format!( - "Server started at http://{}:{}", - config.host, config.port - )); // Log that the server has started + log_info!(logger, "Server started at http:/{}:{}", config.host, config.port); // Log that the server has started // Log whether the cache is enabled based on the config setting if config.cache { - logger.info("Cache Enabled".to_string()); + log_info!(logger, "Cache enabled"); } // If proxy-only mode is enabled, issue a warning that local paths won't be used if proxy_only { - logger - .warn("WARNING: All requests are proxied to /. Local paths won't be used.".to_string()); + log_warn!(logger, "WARNING: All requests are proxied to /. Local paths won't be used."); } // Create separate loggers for each component (proxy, cache, and HTTP) @@ -182,27 +181,21 @@ fn main() { let req_method = req.method.to_str(); // Get the HTTP method (e.g., GET, POST) // Log the incoming request method and path - http_logger.info(format!("Request {} {}", req_method, req.path)); + log_info!(http_logger, "Request {} {}", req_method, req.path); if config.cache { - let cache_start = Instant::now(); // Track cache operation time + let cache_start = Instant::now(); let mut cache_lock = cache.lock().expect("Error locking cache"); if let Some(response) = cache_lock.get(&req) { - cache_logger.debug(format!("cache hit for {}", &req.path)); - let elapsed = start_time.elapsed(); - http_logger.debug(format!( - "Request processed in {:.6}ms", - elapsed.as_secs_f64() * 1000.0 // Log the time taken in milliseconds - )); + log_debug!(cache_logger, "cache hit for {}", req.path); + log_debug!(http_logger, "Request processed in {:.6}ms", + start_time.elapsed().as_secs_f64() * 1000.0); return Box::new(response); } else { - cache_logger.debug(format!("cache miss for {}", &req.path)); + log_debug!(cache_logger, "cache miss for {}", req.path); } - let cache_elapsed = cache_start.elapsed(); - cache_logger.debug(format!( - "Cache operation completed in {:.6}µs", - cache_elapsed.as_micros() - )); + log_debug!(cache_logger, "Cache operation completed in {:.6}µs", + cache_start.elapsed().as_micros()); } let mut ctx = Context { @@ -218,17 +211,14 @@ fn main() { let response = handlers.get_handler(&ctx); if response.is_none() { - logger.error("No handler found for request".to_string()); + log_error!(logger, "No handler found for request"); return HttpResponse::new(HttpStatus::InternalServerError, "content", None); } let response = response.unwrap().run(&mut ctx); - // Log how long the request took to process - let elapsed = start_time.elapsed(); - http_logger.debug(format!( - "Request processed in {:.6}ms", - elapsed.as_secs_f64() * 1000.0 // Log the time taken in milliseconds - )); + log_debug!(http_logger, "Request processed in {:.6}ms", + start_time.elapsed().as_secs_f64() * 1000.0); + response // If content was found, return it with the appropriate headers, otherwise return a 404 });