From 4301b18b024055a64f4058ae853d6c31afb0a6d3 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Thu, 16 May 2024 20:48:38 -0700 Subject: [PATCH 01/10] Clippy fixes and factor out duplicate code --- examples/custom-format.rs | 2 +- examples/ftlog.rs | 3 +-- src/appender/file.rs | 24 +++++++++----------- src/lib.rs | 47 +++++++++++++++++---------------------- tests/local_tz.rs | 3 +-- tests/utc.rs | 3 +-- 6 files changed, 34 insertions(+), 48 deletions(-) diff --git a/examples/custom-format.rs b/examples/custom-format.rs index db0e4df..1bcac3d 100644 --- a/examples/custom-format.rs +++ b/examples/custom-format.rs @@ -41,7 +41,7 @@ fn init() -> LoggerGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "{}@{}||{}:{}[{}] {}", - self.thread.as_ref().map(|x| x.as_str()).unwrap_or(""), + self.thread.as_deref().unwrap_or(""), self.module_path.unwrap_or(""), self.file.unwrap_or(""), self.line.unwrap_or(0), diff --git a/examples/ftlog.rs b/examples/ftlog.rs index bc863a9..b0797f1 100644 --- a/examples/ftlog.rs +++ b/examples/ftlog.rs @@ -1,6 +1,5 @@ use ftlog::{ - appender::{file::Period, FileAppender}, - info, LoggerGuard, + appender::{file::Period, FileAppender}, LoggerGuard, }; use log::LevelFilter; use time::Duration; diff --git a/src/appender/file.rs b/src/appender/file.rs index 0204647..9a04566 100644 --- a/src/appender/file.rs +++ b/src/appender/file.rs @@ -160,10 +160,8 @@ impl< let del_msg = clean_expire_log(p, period, expire); if !del_msg.is_empty() { file.write_fmt(format_args!("Log file deleted: {}", del_msg)) - .expect(&format!( - "Write msg to \"{}\" failed", - path.to_string_lossy() - )); + .unwrap_or_else(|_| panic!("Write msg to \"{}\" failed", + path.to_string_lossy())); } FileAppender { file, @@ -185,7 +183,7 @@ impl< OpenOptions::new() .create(true) .append(true) - .open(&path) + .open(path) .unwrap(), ); FileAppender { @@ -207,10 +205,8 @@ impl< .create(true) .append(true) .open(&builder.path) - .expect(&format!( - "Fail to create log file: {}", - builder.path.to_string_lossy() - )), + .unwrap_or_else(|_| panic!("Fail to create log file: {}", + builder.path.to_string_lossy())), ), path: builder.path, rotate: None, @@ -295,7 +291,7 @@ impl FileAppender { match timezone { LogTimezone::Local => local_timezone(), LogTimezone::Utc => UtcOffset::UTC, - LogTimezone::Fixed(offset) => offset.clone(), + LogTimezone::Fixed(offset) => *offset, } } @@ -367,10 +363,10 @@ fn clean_expire_log(path: PathBuf, rotate_period: Period, keep_duration: Duratio .filter(|x| { let p = x.path(); let name = p.file_stem().unwrap().to_string_lossy(); - if let Some((stem, time)) = name.rsplit_once("-") { + if let Some((stem, time)) = name.rsplit_once('-') { let check = |(ix, x): (usize, char)| match ix { 8 => x == 'T', - _ => x.is_digit(10), + _ => x.is_ascii_digit(), }; let len = match rotate_period { Period::Minute => time.len() == 13, @@ -422,9 +418,9 @@ impl Write for FileAppender { let path = Self::file(&self.path, *period, &self.timezone); // remove outdated log files if let Some(keep_duration) = keep { - let keep_duration = keep_duration.clone(); + let keep_duration = *keep_duration; let path = self.path.clone(); - let period = period.clone(); + let period = *period; std::thread::spawn(move || { let del_msg = clean_expire_log(path, period, keep_duration); if !del_msg.is_empty() { diff --git a/src/lib.rs b/src/lib.rs index 3b5b452..df54e47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -375,7 +375,7 @@ struct LogMsg { impl LogMsg { fn write( self, - filters: &Vec, + filters: &[Directive], appenders: &mut HashMap<&'static str, Box>, root: &mut Box, root_level: LevelFilter, @@ -407,6 +407,13 @@ impl LogMsg { root }; + let delay = duration(self.time, now); + let utc_datetime = to_utc(self.time); + + let offset_datetime = offset + .map(|o| utc_datetime.to_offset(o)) + .unwrap_or(utc_datetime); + let s: String; if self.limit > 0 { let missed_entry = missed_log.entry(self.limit_key).or_insert_with(|| 0); if let Some(last) = last_log.get(&self.limit_key) { @@ -416,14 +423,8 @@ impl LogMsg { } } last_log.insert(self.limit_key, now); - let delay = duration(self.time, now); - let utc_datetime = to_utc(self.time); - - let offset_datetime = offset - .map(|o| utc_datetime.to_offset(o)) - .unwrap_or(utc_datetime); - let s = format!( + s = format!( "{} {}ms {} {}\n", offset_datetime .format(&time_format) @@ -434,17 +435,9 @@ impl LogMsg { *missed_entry, msg ); - if let Err(e) = writer.write_all(s.as_bytes()) { - eprintln!("logger write message failed: {}", e); - }; *missed_entry = 0; } else { - let delay = duration(self.time, now); - let utc_datetime = to_utc(self.time); - let offset_datetime = offset - .map(|o| utc_datetime.to_offset(o)) - .unwrap_or(utc_datetime); - let s = format!( + s = format!( "{} {}ms {}\n", offset_datetime .format(&time_format) @@ -454,10 +447,10 @@ impl LogMsg { delay.as_millis(), msg ); - if let Err(e) = writer.write_all(s.as_bytes()) { - eprintln!("logger write message failed: {}", e); - }; } + if let Err(e) = writer.write_all(s.as_bytes()) { + eprintln!("logger write message failed: {}", e); + }; } } @@ -553,14 +546,14 @@ impl FtLogFormat for FtLogFormatter { thread: std::thread::current().name().map(|n| n.to_string()), file: record .file_static() - .map(|s| Cow::Borrowed(s)) + .map(Cow::Borrowed) .or_else(|| record.file().map(|s| Cow::Owned(s.to_owned()))) .unwrap_or(Cow::Borrowed("")), line: record.line(), args: record .args() .as_str() - .map(|s| Cow::Borrowed(s)) + .map(Cow::Borrowed) .unwrap_or_else(|| Cow::Owned(format!("{}", record.args()))), }) } @@ -579,7 +572,7 @@ impl Display for Message { f.write_str(&format!( "{} {} [{}:{}] {}", self.level, - self.thread.as_ref().map(|x| x.as_str()).unwrap_or(""), + self.thread.as_deref().unwrap_or(""), self.file, self.line.unwrap_or(0), self.args @@ -676,14 +669,14 @@ impl Log for Logger { }; let msg = LoggerInput::LogMsg(LogMsg { time: now(), - msg: msg, + msg, target: record.target().to_owned(), level: record.level(), limit, limit_key, }); if self.block { - if let Err(_) = self.queue.send(msg) { + if self.queue.send(msg).is_err() { let stop = self.stopped.load(Ordering::SeqCst); if !stop { eprintln!("logger queue closed when logging, this is a bug"); @@ -919,8 +912,8 @@ impl Builder { if appender.is_some() || level.is_some() { self.filters.push(Directive { path: module_path, - appender: appender, - level: level, + appender, + level, }); } self diff --git a/tests/local_tz.rs b/tests/local_tz.rs index a72350f..bfe6ede 100644 --- a/tests/local_tz.rs +++ b/tests/local_tz.rs @@ -5,7 +5,7 @@ use std::{ use ftlog::appender::{Duration, FileAppender, Period}; -pub fn setup() -> () { +pub fn setup() { let logger = ftlog::Builder::new() .bounded(10000, true) .root(FileAppender::new("./root.log")) @@ -24,7 +24,6 @@ pub fn setup() -> () { fn clean(dir: &str) { for file in read_dir(dir) .unwrap() - .into_iter() .filter_map(|x| x.ok()) .filter(|f| f.file_type().unwrap().is_file()) { diff --git a/tests/utc.rs b/tests/utc.rs index 70d3008..3ea6050 100644 --- a/tests/utc.rs +++ b/tests/utc.rs @@ -5,7 +5,7 @@ use std::{ use ftlog::appender::{Duration, FileAppender, Period}; -pub fn setup() -> () { +pub fn setup() { let logger = ftlog::Builder::new() .bounded(10000, true) .root(FileAppender::new("./root.log")) @@ -25,7 +25,6 @@ pub fn setup() -> () { fn clean(dir: &str) { for file in read_dir(dir) .unwrap() - .into_iter() .filter_map(|x| x.ok()) .filter(|f| f.file_type().unwrap().is_file()) { From 4540ca06d8920f437e0835c96cf1c43a2de72ca0 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Fri, 17 May 2024 11:32:08 -0700 Subject: [PATCH 02/10] Flexible filtering with a closure --- examples/custom-format.rs | 21 +++++++--- examples/ftlog.rs | 8 +++- examples/multi-dest.rs | 5 ++- src/lib.rs | 86 +++++++++++++++++++++------------------ tests/local_tz.rs | 4 +- tests/utc.rs | 4 +- 6 files changed, 76 insertions(+), 52 deletions(-) diff --git a/examples/custom-format.rs b/examples/custom-format.rs index 1bcac3d..5958d45 100644 --- a/examples/custom-format.rs +++ b/examples/custom-format.rs @@ -2,9 +2,9 @@ use std::fmt::Display; use ftlog::{ appender::{file::Period, FileAppender}, - info, FtLogFormat, LoggerGuard, + info, FtLogFormat, LoggerGuard, Record, }; -use log::{Level, LevelFilter, Record}; +use log::{Level, LevelFilter}; use time::Duration; fn init() -> LoggerGuard { // Custom log style. @@ -41,7 +41,7 @@ fn init() -> LoggerGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "{}@{}||{}:{}[{}] {}", - self.thread.as_deref().unwrap_or(""), + self.thread.as_ref().map(|x| x.as_str()).unwrap_or(""), self.module_path.unwrap_or(""), self.file.unwrap_or(""), self.line.unwrap_or(0), @@ -72,11 +72,20 @@ fn init() -> LoggerGuard { ) // ---------- configure additional filter ---------- // write to "ftlog-appender" appender, with different level filter - .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) + .filter( + |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, + "ftlog-appender", + ) // write to root appender, but with different level filter - .filter("ftlog", None, LevelFilter::Trace) + .filter( + |_msg, level, target| target == "ftlog" && level == LevelFilter::Trace, + "ftlog", + ) // write to "ftlog" appender, with default level filter - .filter("ftlog::appender::file", "ftlog", None) + .filter( + |_msg, _level, target| target == "ftlog::appender::file", + "ftlog", + ) // ---------- configure additional appender ---------- // new appender .appender("ftlog-appender", FileAppender::new("ftlog-appender.log")) diff --git a/examples/ftlog.rs b/examples/ftlog.rs index b0797f1..df69b0b 100644 --- a/examples/ftlog.rs +++ b/examples/ftlog.rs @@ -1,5 +1,6 @@ use ftlog::{ - appender::{file::Period, FileAppender}, LoggerGuard, + appender::{file::Period, FileAppender}, + LoggerGuard, }; use log::LevelFilter; use time::Duration; @@ -17,7 +18,10 @@ fn init() -> LoggerGuard { // define root appender, pass None would write to stderr .root(writer) // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" - .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) + .filter( + |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, + "ftlog-appender", + ) .appender("ftlog-appender", FileAppender::new("ftlog-appender.log")) .try_init() .expect("logger build or set failed") diff --git a/examples/multi-dest.rs b/examples/multi-dest.rs index 6b51339..769ddd2 100644 --- a/examples/multi-dest.rs +++ b/examples/multi-dest.rs @@ -21,7 +21,10 @@ fn init() -> LoggerGuard { Box::new(std::io::stdout()), ])) // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" - .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) + .filter( + |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, + "ftlog-appender", + ) .appender("ftlog-appender", FileAppender::new("ftlog-appender.log")) .try_init() .expect("logger build or set failed") diff --git a/src/lib.rs b/src/lib.rs index df54e47..8e5f8b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,7 @@ //! // level filter for root appender //! .root_log_level(LevelFilter::Warn) //! // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" -//! .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) +//! .filter(|msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, "ftlog-appender") //! .appender("ftlog-appender", FileAppender::new("ftlog-appender.log")) //! .try_init() //! .expect("logger build or set failed"); @@ -372,6 +372,7 @@ struct LogMsg { limit: u32, limit_key: u64, } + impl LogMsg { fn write( self, @@ -391,11 +392,11 @@ impl LogMsg { let now = now(); - let writer = if let Some(filter) = filters.iter().find(|x| self.target.starts_with(x.path)) + // Find an appender filter if one exists + let writer = if let Some(filter) = filters + .iter() + .find(|x| (*x.filter)(&self.msg, self.level, &self.target)) { - if filter.level.map(|l| l < self.level).unwrap_or(false) { - return; - } filter .appender .and_then(|n| appenders.get_mut(n)) @@ -546,14 +547,14 @@ impl FtLogFormat for FtLogFormatter { thread: std::thread::current().name().map(|n| n.to_string()), file: record .file_static() - .map(Cow::Borrowed) + .map(|s| Cow::Borrowed(s)) .or_else(|| record.file().map(|s| Cow::Owned(s.to_owned()))) .unwrap_or(Cow::Borrowed("")), line: record.line(), args: record .args() .as_str() - .map(Cow::Borrowed) + .map(|s| Cow::Borrowed(s)) .unwrap_or_else(|| Cow::Owned(format!("{}", record.args()))), }) } @@ -607,6 +608,7 @@ impl Drop for LoggerGuard { pub struct Logger { format: Box, level: LevelFilter, + filters: Vec bool + Send + Sync>>, queue: Sender, notification: Receiver, block: bool, @@ -654,7 +656,15 @@ impl Log for Logger { .and_then(|x| x.to_u64()) .unwrap_or(0) as u32; - let msg = self.format.msg(record); + // This will short circuit if any of the filters return false, meaning don't keep this record. + if !self.filters.is_empty() { + if self.filters.iter().all(|filter| filter(record)) { + // Drop this log record + println!("Dropping this record {:?}", record); + return; + } + } + let limit_key = if limit == 0 { 0 } else { @@ -667,6 +677,7 @@ impl Log for Logger { record.line().unwrap_or(0).hash(&mut b); b.finish() }; + let msg = self.format.msg(record); let msg = LoggerInput::LogMsg(LogMsg { time: now(), msg, @@ -745,11 +756,11 @@ struct BoundedChannelOption { /// )) /// // ---------- configure additional filter ---------- /// // write to "ftlog-appender" appender, with different level filter -/// .filter("ftlog::appender", "ftlog-appender", LevelFilter::Error) +/// .filter(|msg, level, target| target == "ftlog::appender"&& level == LevelFilter::Error, "ftlog-appender") /// // write to root appender, but with different level filter -/// .filter("ftlog", None, LevelFilter::Trace) +/// .filter(|msg, level, target| target == "ftlog" && level == LevelFilter::Trace, None) /// // write to "ftlog" appender, with default level filter -/// .filter("ftlog::appender::file", "ftlog", None) +/// .filter(|msg, level, target| target == "ftlog::appender::file", "ftlog") /// // ---------- configure additional appender ---------- /// // new appender /// .appender("ftlog-appender", FileAppender::new("ftlog-appender.log")) @@ -771,6 +782,7 @@ pub struct Builder { root: Box, appenders: HashMap<&'static str, Box>, filters: Vec, + drop_filters: Vec bool + Send + Sync>>, bounded_channel_option: Option, timezone: LogTimezone, } @@ -782,8 +794,7 @@ pub fn builder() -> Builder { } struct Directive { - path: &'static str, - level: Option, + filter: Box bool + Send>, appender: Option<&'static str>, } /// timezone for log @@ -803,6 +814,7 @@ impl Builder { /// Create a ftlog builder with default settings: /// - global log level: INFO /// - root log level: INFO + /// - no drop filters /// - default formatter: `FtLogFormatter` /// - output to stderr /// - bounded channel between worker thread and log thread, with a size limit of 100_000 @@ -816,6 +828,7 @@ impl Builder { root: Box::new(stderr()) as Box, appenders: HashMap::new(), filters: Vec::new(), + drop_filters: Vec::new(), bounded_channel_option: Some(BoundedChannelOption { size: 100_000, block: false, @@ -840,6 +853,16 @@ impl Builder { self } + /// This will drop log records before they are sent into the channel. + #[inline] + pub fn drop_filters(mut self, filter: F) -> Builder + where + F: Fn(&Record) -> bool + Send + Sync + 'static, + { + self.drop_filters.push(Box::new(filter)); + self + } + /// bound channel between worker thread and log thread /// /// When `block_when_full` is true, it will block current thread where @@ -895,25 +918,23 @@ impl Builder { } /// Add a filter to redirect log to different output - /// target (e.g. stderr, stdout, different files). + /// target (e.g. stderr, stdout, different files). The filter closure takes in a + /// message, a level and a target. The filter must return true if the log message + /// should use this appender. Note that this will use the first match that succeeds. /// /// **ATTENTION**: level more verbose than `Builder::max_log_level` will be ignored. /// Say we configure `max_log_level` to INFO, and even if filter's level is set to DEBUG, /// ftlog will still log up to INFO. #[inline] - pub fn filter>, L: Into>>( - mut self, - module_path: &'static str, - appender: A, - level: L, - ) -> Builder { + pub fn filter>, F>(mut self, filter: F, appender: A) -> Builder + where + F: Fn(&dyn Display, Level, &str) -> bool + Send + Sync + 'static, + { let appender = appender.into(); - let level = level.into(); - if appender.is_some() || level.is_some() { + if appender.is_some() { self.filters.push(Directive { - path: module_path, + filter: Box::new(filter), appender, - level, }); } self @@ -996,10 +1017,7 @@ impl Builder { ) .unwrap() }); - let mut filters = self.filters; - // sort filters' paths to ensure match for longest path - filters.sort_by(|a, b| a.path.len().cmp(&b.path.len())); - filters.reverse(); + let filters = self.filters; // check appender name in filters are all valid for appender_name in filters.iter().filter_map(|x| x.appender) { if !self.appenders.contains_key(appender_name) { @@ -1026,17 +1044,6 @@ impl Builder { let mut appenders = self.appenders; let filters = filters; - for filter in &filters { - if let Some(level) = filter.level { - if global_level < level { - warn!( - "Logs with level more verbose than {} will be ignored in `{}` ", - global_level, filter.path, - ); - } - } - } - let mut root = self.root; let mut last_log = HashMap::default(); let mut missed_log = HashMap::default(); @@ -1121,6 +1128,7 @@ impl Builder { .unwrap_or(false); Ok(Logger { format: self.format, + filters: self.drop_filters, level: global_level, queue: sync_sender, notification: notification_receiver, diff --git a/tests/local_tz.rs b/tests/local_tz.rs index bfe6ede..02b45e2 100644 --- a/tests/local_tz.rs +++ b/tests/local_tz.rs @@ -9,9 +9,9 @@ pub fn setup() { let logger = ftlog::Builder::new() .bounded(10000, true) .root(FileAppender::new("./root.log")) - .filter("rotate", "rotate", None) + .filter(|_msg, _level, target| target == "rotate", "rotate") .appender("rotate", FileAppender::rotate("rotate.log", Period::Minute)) - .filter("expire", "expire", None) + .filter(|_msg, _level, target| target == "expire", "expire") .appender( "expire", FileAppender::rotate_with_expire("expire.log", Period::Day, Duration::days(7)), diff --git a/tests/utc.rs b/tests/utc.rs index 3ea6050..a4f6804 100644 --- a/tests/utc.rs +++ b/tests/utc.rs @@ -10,9 +10,9 @@ pub fn setup() { .bounded(10000, true) .root(FileAppender::new("./root.log")) // .utc() - .filter("rotate", "rotate", None) + .filter(|_msg, _level, target| target == "rotate", "rotate") .appender("rotate", FileAppender::rotate("rotate.log", Period::Minute)) - .filter("expire", "expire", None) + .filter(|_msg, _level, target| target == "expire", "expire") .appender( "expire", FileAppender::rotate_with_expire("expire.log", Period::Day, Duration::days(7)), From 92b526f73a9e0b000188421e938e7facd0db09e3 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Thu, 23 May 2024 08:51:29 -0700 Subject: [PATCH 03/10] cargo fmt --- src/appender/file.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/appender/file.rs b/src/appender/file.rs index 9a04566..6e03202 100644 --- a/src/appender/file.rs +++ b/src/appender/file.rs @@ -160,8 +160,9 @@ impl< let del_msg = clean_expire_log(p, period, expire); if !del_msg.is_empty() { file.write_fmt(format_args!("Log file deleted: {}", del_msg)) - .unwrap_or_else(|_| panic!("Write msg to \"{}\" failed", - path.to_string_lossy())); + .unwrap_or_else(|_| { + panic!("Write msg to \"{}\" failed", path.to_string_lossy()) + }); } FileAppender { file, @@ -205,8 +206,12 @@ impl< .create(true) .append(true) .open(&builder.path) - .unwrap_or_else(|_| panic!("Fail to create log file: {}", - builder.path.to_string_lossy())), + .unwrap_or_else(|_| { + panic!( + "Fail to create log file: {}", + builder.path.to_string_lossy() + ) + }), ), path: builder.path, rotate: None, From 60a90cb1b8ecaa4e312cef2f830cb1605987d204 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Thu, 18 Jul 2024 17:18:36 -0700 Subject: [PATCH 04/10] Remove time prepending Callers can add timestamp support as a field during log message formatting. --- Cargo.toml | 46 ++++++++++++------------- src/lib.rs | 99 ++++++------------------------------------------------ 2 files changed, 34 insertions(+), 111 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e192d2..a26e940 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ftlog" version = "0.2.13" edition = "2021" -authors = [ "Non-convex Tech" ] +authors = ["Non-convex Tech"] license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/nonconvextech/ftlog" @@ -10,41 +10,41 @@ documentation = "https://docs.rs/ftlog" description = """ An asynchronous logging library for high performance """ -categories = [ "development-tools::debugging" ] -keywords = [ "logging" ] -exclude = [ ".standard-version", ".versionrc", ".github" ] +categories = ["development-tools::debugging"] +keywords = ["logging"] +exclude = [".standard-version", ".versionrc", ".github"] [features] -default = [ "random_drop" ] -tsc = [ "minstant", "once_cell" ] -random_drop = [ "fastrand" ] +default = ["random_drop"] +tsc = ["minstant", "once_cell"] +random_drop = ["fastrand"] [dependencies] +arc-swap = "1" crossbeam-channel = "0.5.0" hashbrown = "0.14" -arc-swap = "1" nohash-hasher = "0.2" typed-builder = "0.16" - [dependencies.fastrand] - version = "2" - optional = true +[dependencies.fastrand] +version = "2" +optional = true - [dependencies.time] - version = "0.3" - features = [ "local-offset", "formatting" ] +[dependencies.log] +version = "0.4" +features = ["std", "kv_unstable"] - [dependencies.minstant] - version = "0.1" - optional = true +[dependencies.minstant] +version = "0.1" +optional = true - [dependencies.once_cell] - version = "1" - optional = true +[dependencies.once_cell] +version = "1" +optional = true - [dependencies.log] - version = "0.4" - features = [ "std", "kv_unstable" ] +[dependencies.time] +version = "0.3" +features = ["local-offset", "formatting"] [target."cfg(target_family = \"unix\")".dependencies.tz-rs] version = "0.6.14" diff --git a/src/lib.rs b/src/lib.rs index 8e5f8b3..f5785e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,11 +76,6 @@ //! .expire(Duration::days(7)) //! .build(), //! ) -//! // timezone of log message timestamp, use local by default -//! // .local_timezone() -//! // or use fiexed timezone for better throughput, since retrieving timezone is a time consuming operation -//! // this does not affect worker threads (that call log), but can boost log thread performance (higher throughput). -//! .fixed_timezone(time::UtcOffset::current_local_offset().unwrap()) //! // level filter for root appender //! .root_log_level(LevelFilter::Warn) //! // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" @@ -312,6 +307,7 @@ use tm::{duration, now, to_utc, Time}; #[cfg(not(feature = "tsc"))] mod tm { + use super::*; pub type Time = std::time::SystemTime; @@ -365,7 +361,6 @@ fn local_timezone() -> UtcOffset { } struct LogMsg { - time: Time, msg: Box, level: Level, target: String, @@ -382,8 +377,6 @@ impl LogMsg { root_level: LevelFilter, missed_log: &mut HashMap>, last_log: &mut HashMap>, - offset: Option, - time_format: &time::format_description::OwnedFormatItem, ) { let msg = self.msg.to_string(); if msg.is_empty() { @@ -408,12 +401,6 @@ impl LogMsg { root }; - let delay = duration(self.time, now); - let utc_datetime = to_utc(self.time); - - let offset_datetime = offset - .map(|o| utc_datetime.to_offset(o)) - .unwrap_or(utc_datetime); let s: String; if self.limit > 0 { let missed_entry = missed_log.entry(self.limit_key).or_insert_with(|| 0); @@ -425,29 +412,10 @@ impl LogMsg { } last_log.insert(self.limit_key, now); - s = format!( - "{} {}ms {} {}\n", - offset_datetime - .format(&time_format) - .unwrap_or_else(|_| offset_datetime - .format(&time::format_description::well_known::Rfc3339) - .unwrap()), - delay.as_millis(), - *missed_entry, - msg - ); + s = format!("{} {}\n", *missed_entry, msg); *missed_entry = 0; } else { - s = format!( - "{} {}ms {}\n", - offset_datetime - .format(&time_format) - .unwrap_or_else(|_| offset_datetime - .format(&time::format_description::well_known::Rfc3339) - .unwrap()), - delay.as_millis(), - msg - ); + s = format!("{}\n", msg); } if let Err(e) = writer.write_all(s.as_bytes()) { eprintln!("logger write message failed: {}", e); @@ -543,6 +511,7 @@ impl FtLogFormat for FtLogFormatter { #[inline] fn msg(&self, record: &Record) -> Box { Box::new(Message { + time: now(), level: record.level(), thread: std::thread::current().name().map(|n| n.to_string()), file: record @@ -561,6 +530,7 @@ impl FtLogFormat for FtLogFormatter { } struct Message { + time: Time, level: Level, thread: Option, file: Cow<'static, str>, @@ -570,8 +540,13 @@ struct Message { impl Display for Message { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let utc_datetime = to_utc(self.time); + f.write_str(&format!( - "{} {} [{}:{}] {}", + "{} {} {} [{}:{}] {}", + utc_datetime + .format(&time::format_description::well_known::Rfc3339) + .unwrap(), self.level, self.thread.as_deref().unwrap_or(""), self.file, @@ -679,7 +654,6 @@ impl Log for Logger { }; let msg = self.format.msg(record); let msg = LoggerInput::LogMsg(LogMsg { - time: now(), msg, target: record.target().to_owned(), level: record.level(), @@ -784,7 +758,6 @@ pub struct Builder { filters: Vec, drop_filters: Vec bool + Send + Sync>>, bounded_channel_option: Option, - timezone: LogTimezone, } /// Handy function to get ftlog builder @@ -834,7 +807,6 @@ impl Builder { block: false, print: true, }), - timezone: LogTimezone::Local, time_format: None, } } @@ -967,56 +939,11 @@ impl Builder { self } - #[inline] - /// Log with timestamp of local timezone - /// - /// Timezone is fixed after logger setup for the following reasons: - /// 1. `time` v0.3 currently do not allow access to local offset for multithread process - /// in unix-like OS. - /// 1. timezone retrieval from OS is quite slow (around several microsecond) compare with - /// utc timestamp retrieval (around tens of nanoseconds) - pub fn local_timezone(mut self) -> Builder { - self.timezone = LogTimezone::Local; - self - } - - #[inline] - /// Log with timestamp of UTC timezone - pub fn utc(mut self) -> Builder { - self.timezone = LogTimezone::Utc; - self - } - - #[inline] - /// Log with timestamp of fixed timezone - pub fn fixed_timezone(mut self, timezone: UtcOffset) -> Builder { - self.timezone = LogTimezone::Fixed(timezone); - self - } - - #[inline] - /// Specify the timezone of log messages - pub fn timezone(mut self, timezone: LogTimezone) -> Builder { - self.timezone = timezone; - self - } - /// Finish building ftlog logger /// /// The call spawns a log thread to formatting log message into string, /// and write to output target. pub fn build(self) -> Result { - let offset = match self.timezone { - LogTimezone::Local => Some(local_timezone()), - LogTimezone::Utc => None, - LogTimezone::Fixed(offset) => Some(offset), - }; - let time_format = self.time_format.unwrap_or_else(|| { - time::format_description::parse_owned::<1>( - "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]+[offset_hour]", - ) - .unwrap() - }); let filters = self.filters; // check appender name in filters are all valid for appender_name in filters.iter().filter_map(|x| x.appender) { @@ -1059,8 +986,6 @@ impl Builder { root_level, &mut missed_log, &mut last_log, - offset, - &time_format, ); } Ok(LoggerInput::Flush) => { @@ -1074,8 +999,6 @@ impl Builder { root_level, &mut missed_log, &mut last_log, - offset, - &time_format, ) } else { break 'queue; From 9f5e087ec2e2de2fde229f9fcb3eb77e6a5af421 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Thu, 8 Aug 2024 16:28:56 -0700 Subject: [PATCH 05/10] Use env_logger module filter --- Cargo.toml | 1 + examples/env_log.rs | 30 ++++++++++++++++++++++++++++++ src/lib.rs | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 examples/env_log.rs diff --git a/Cargo.toml b/Cargo.toml index a26e940..a432bf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ random_drop = ["fastrand"] [dependencies] arc-swap = "1" crossbeam-channel = "0.5.0" +env_filter = "0.1" hashbrown = "0.14" nohash-hasher = "0.2" typed-builder = "0.16" diff --git a/examples/env_log.rs b/examples/env_log.rs new file mode 100644 index 0000000..d480c94 --- /dev/null +++ b/examples/env_log.rs @@ -0,0 +1,30 @@ +use ftlog::{appender::FileAppender, LoggerGuard}; +use log::{info, LevelFilter}; + +fn init() -> LoggerGuard { + // Rotate every day, clean stale logs that were modified 7 days ago on each rotation + let writer = FileAppender::builder() + .path("./env_log.log") + .build(); + ftlog::Builder::new() + // global max log level + .max_log_level(LevelFilter::Info) + .use_env_filter() + // define root appender, pass None would write to stderr + .root(writer) + // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" + .try_init() + .expect("logger build or set failed") +} + +fn main() { + // RUST_LOG=env_log cargo run --example env_log or RUST_LOG=info cargo run --example env_log will show log lines + let _guard = init(); + info!("Hello, world!"); + for i in 0..120 { + info!("running {}!", i); + info!(limit=3000i64; "limit running{} !", i); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + std::thread::sleep(std::time::Duration::from_secs(5)); +} diff --git a/src/lib.rs b/src/lib.rs index f5785e4..f76e908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -582,6 +582,7 @@ impl Drop for LoggerGuard { /// ftlog global logger pub struct Logger { format: Box, + env_filter: Option, level: LevelFilter, filters: Vec bool + Send + Sync>>, queue: Sender, @@ -639,6 +640,12 @@ impl Log for Logger { return; } } + if let Some(filter) = &self.env_filter { + if !filter.matches(record) { + // Drop this log record + return; + } + } let limit_key = if limit == 0 { 0 @@ -749,6 +756,7 @@ struct BoundedChannelOption { /// local timezone offset forever. Thus timestamp in log does not aware of timezone /// change by OS. pub struct Builder { + env_filter: bool, format: Box, time_format: Option, level: Option, @@ -795,6 +803,7 @@ impl Builder { /// - log with timestamp of local timezone pub fn new() -> Builder { Builder { + env_filter: false, format: Box::new(FtLogFormatter), level: None, root_level: None, @@ -912,6 +921,14 @@ impl Builder { self } + /// Use the RUST_LOG env variable to filter logs. Uses the same syntax that env_logger + /// supports because it uses the same Filter internally. + pub fn use_env_filter(mut self) -> Builder { + self.env_filter = true; + + self + } + #[inline] /// Configure the default log output target. /// @@ -952,13 +969,26 @@ impl Builder { } } let global_level = self.level.unwrap_or(LevelFilter::Info); - let root_level = self.root_level.unwrap_or(global_level); + let mut root_level = self.root_level.unwrap_or(global_level); if global_level < root_level { warn!( "Logs with level more verbose than {} will be ignored", global_level, ); } + let env_filter = if self.env_filter { + let mut builder = env_filter::Builder::new(); + // Parse a logging filter from an environment variable. + if let Ok(rust_log) = std::env::var("RUST_LOG") { + builder.parse(&rust_log); + } + let filter = builder.build(); + // Set the root level filter to this + root_level = filter.filter(); + Some(filter) + } else { + None + }; let (sync_sender, receiver) = match &self.bounded_channel_option { None => unbounded(), @@ -1050,6 +1080,7 @@ impl Builder { .map(|x| x.print) .unwrap_or(false); Ok(Logger { + env_filter, format: self.format, filters: self.drop_filters, level: global_level, From 090bb34b3151f93818f1eb95f7dbf7550568c409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E5=87=B8=E7=A7=91=E6=8A=80?= <116966725+nonconvextech@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:45:14 +0800 Subject: [PATCH 06/10] Update lib.rs --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 534ff34..d67828d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -418,7 +418,7 @@ impl LogMsg { format!("{} {}\n", *missed_entry, msg) } else { format!("{}\n", msg) - } + }; if let Err(e) = writer.write_all(s.as_bytes()) { eprintln!("logger write message failed: {}", e); }; From ae522379d4e0f66399aa9ff088c6478894012dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E5=87=B8=E7=A7=91=E6=8A=80?= <116966725+nonconvextech@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:46:38 +0800 Subject: [PATCH 07/10] Update env_log.rs --- examples/env_log.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/env_log.rs b/examples/env_log.rs index d480c94..feb9290 100644 --- a/examples/env_log.rs +++ b/examples/env_log.rs @@ -3,9 +3,7 @@ use log::{info, LevelFilter}; fn init() -> LoggerGuard { // Rotate every day, clean stale logs that were modified 7 days ago on each rotation - let writer = FileAppender::builder() - .path("./env_log.log") - .build(); + let writer = FileAppender::builder().path("./env_log.log").build(); ftlog::Builder::new() // global max log level .max_log_level(LevelFilter::Info) From d0b4e7ccf881dd5277b760506aa7f78943e1f052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E5=87=B8=E7=A7=91=E6=8A=80?= <116966725+nonconvextech@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:47:23 +0800 Subject: [PATCH 08/10] style: cargo fmt --- src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d67828d..019c085 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,7 +378,6 @@ impl LogMsg { missed_log: &mut HashMap>, last_log: &mut HashMap>, ) { - // Find an appender filter if one exists let writer = if let Some(filter) = filters .iter() @@ -395,15 +394,12 @@ impl LogMsg { root }; - let msg = self.msg.to_string(); if msg.is_empty() { return; } let now = now(); - - let s = if self.limit > 0 { let missed_entry = missed_log.entry(self.limit_key).or_insert_with(|| 0); if let Some(last) = last_log.get(&self.limit_key) { From 7963e6cb3eeb9c084eafd9ab5cfe5a5d9151276b Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Thu, 7 Nov 2024 08:57:08 -0800 Subject: [PATCH 09/10] reintroduce 'filter' and change the closure-based fn to 'filter_with' --- src/lib.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 019c085..0aca42a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -896,16 +896,41 @@ impl Builder { self } + /// Add a filter to redirect log to different output + /// target (e.g. stderr, stdout, different files). + /// + /// **ATTENTION**: Any level more verbose than `Builder::max_log_level` will be ignored. + /// If we configure `max_log_level` to INFO, and even if filter's level is set to DEBUG, + /// ftlog will still log up to INFO. + #[inline] + pub fn filter>, L: Into>>( + mut self, + module_path: &'static str, + appender: A, + level: L, + ) -> Builder { + let appender = appender.into(); + let level = level.into(); + if appender.is_some() || level.is_some() { + self.filters.push(Directive { + path: module_path, + appender: appender, + level: level, + }); + } + self + } + /// Add a filter to redirect log to different output /// target (e.g. stderr, stdout, different files). The filter closure takes in a /// message, a level and a target. The filter must return true if the log message /// should use this appender. Note that this will use the first match that succeeds. /// - /// **ATTENTION**: level more verbose than `Builder::max_log_level` will be ignored. - /// Say we configure `max_log_level` to INFO, and even if filter's level is set to DEBUG, + /// **ATTENTION**: Any level more verbose than `Builder::max_log_level` will be ignored. + /// If we configure `max_log_level` to INFO, and even if filter's level is set to DEBUG, /// ftlog will still log up to INFO. #[inline] - pub fn filter>, F>(mut self, filter: F, appender: A) -> Builder + pub fn filter_with>, F>(mut self, filter: F, appender: A) -> Builder where F: Fn(&dyn Display, Level, &str) -> bool + Send + Sync + 'static, { From 6a51de22c1f9c6a09323a6173301dbeae0fe4235 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Thu, 7 Nov 2024 09:38:12 -0800 Subject: [PATCH 10/10] restored original filter API. Cleaned up more clippy warnings --- examples/custom-format.rs | 8 ++--- examples/ftlog.rs | 4 +-- examples/multi-dest.rs | 2 +- src/lib.rs | 59 +++++++++++++++++++------------ tests/local_tz.rs | 74 ++++++++++++++++++++++++++++++++++++--- tests/utc.rs | 6 ++-- 6 files changed, 115 insertions(+), 38 deletions(-) diff --git a/examples/custom-format.rs b/examples/custom-format.rs index 5958d45..accb054 100644 --- a/examples/custom-format.rs +++ b/examples/custom-format.rs @@ -41,7 +41,7 @@ fn init() -> LoggerGuard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "{}@{}||{}:{}[{}] {}", - self.thread.as_ref().map(|x| x.as_str()).unwrap_or(""), + self.thread.as_deref().unwrap_or(""), self.module_path.unwrap_or(""), self.file.unwrap_or(""), self.line.unwrap_or(0), @@ -72,17 +72,17 @@ fn init() -> LoggerGuard { ) // ---------- configure additional filter ---------- // write to "ftlog-appender" appender, with different level filter - .filter( + .filter_with( |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, "ftlog-appender", ) // write to root appender, but with different level filter - .filter( + .filter_with( |_msg, level, target| target == "ftlog" && level == LevelFilter::Trace, "ftlog", ) // write to "ftlog" appender, with default level filter - .filter( + .filter_with( |_msg, _level, target| target == "ftlog::appender::file", "ftlog", ) diff --git a/examples/ftlog.rs b/examples/ftlog.rs index df69b0b..b341ef0 100644 --- a/examples/ftlog.rs +++ b/examples/ftlog.rs @@ -6,7 +6,7 @@ use log::LevelFilter; use time::Duration; fn init() -> LoggerGuard { - // Rotate every day, clean stale logs that were modified 7 days ago on each rotation + // Rotate every minute, clean stale logs that were modified 4 minutes ago on each rotation let writer = FileAppender::builder() .path("./current.log") .rotate(Period::Minute) @@ -18,7 +18,7 @@ fn init() -> LoggerGuard { // define root appender, pass None would write to stderr .root(writer) // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" - .filter( + .filter_with( |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, "ftlog-appender", ) diff --git a/examples/multi-dest.rs b/examples/multi-dest.rs index 769ddd2..19058e1 100644 --- a/examples/multi-dest.rs +++ b/examples/multi-dest.rs @@ -21,7 +21,7 @@ fn init() -> LoggerGuard { Box::new(std::io::stdout()), ])) // write logs in ftlog::appender to "./ftlog-appender.log" instead of "./current.log" - .filter( + .filter_with( |_msg, level, target| target == "ftlog::appender" && level == LevelFilter::Error, "ftlog-appender", ) diff --git a/src/lib.rs b/src/lib.rs index 0aca42a..276424e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -514,14 +514,14 @@ impl FtLogFormat for FtLogFormatter { thread: std::thread::current().name().map(|n| n.to_string()), file: record .file_static() - .map(|s| Cow::Borrowed(s)) + .map(Cow::Borrowed) .or_else(|| record.file().map(|s| Cow::Owned(s.to_owned()))) .unwrap_or(Cow::Borrowed("")), line: record.line(), args: record .args() .as_str() - .map(|s| Cow::Borrowed(s)) + .map(Cow::Borrowed) .unwrap_or_else(|| Cow::Owned(format!("{}", record.args()))), }) } @@ -582,7 +582,7 @@ pub struct Logger { format: Box, env_filter: Option, level: LevelFilter, - filters: Vec bool + Send + Sync>>, + filters: Vec, queue: Sender, notification: Receiver, block: bool, @@ -631,13 +631,12 @@ impl Log for Logger { .unwrap_or(0) as u32; // This will short circuit if any of the filters return false, meaning don't keep this record. - if !self.filters.is_empty() { - if self.filters.iter().all(|filter| filter(record)) { - // Drop this log record - println!("Dropping this record {:?}", record); - return; - } + if !self.filters.is_empty() && self.filters.iter().all(|filter| filter(record)) { + // Drop this log record + println!("Dropping this record {:?}", record); + return; } + if let Some(filter) = &self.env_filter { if !filter.matches(record) { // Drop this log record @@ -762,7 +761,7 @@ pub struct Builder { root: Box, appenders: HashMap<&'static str, Box>, filters: Vec, - drop_filters: Vec bool + Send + Sync>>, + drop_filters: Vec, bounded_channel_option: Option, } @@ -772,10 +771,17 @@ pub fn builder() -> Builder { Builder::new() } +/// Type alias for the closure intended to filter drops +pub type DropFilterFn = Box bool + Send + Sync>; + +/// Type alias for the closure for filtering/redirecting logs to appenders +pub type AppenderFilterFn = Box bool + Send>; + struct Directive { - filter: Box bool + Send>, + filter: AppenderFilterFn, appender: Option<&'static str>, } + /// timezone for log pub enum LogTimezone { /// local timezone @@ -864,9 +870,9 @@ impl Builder { /// thread is bounded, and set to discard excessive log messages #[inline] pub fn print_omitted_count(mut self, print: bool) -> Builder { - self.bounded_channel_option - .as_mut() - .map(|o| o.print = print); + if let Some(o) = self.bounded_channel_option.as_mut() { + o.print = print; + } self } @@ -904,21 +910,24 @@ impl Builder { /// ftlog will still log up to INFO. #[inline] pub fn filter>, L: Into>>( - mut self, + self, module_path: &'static str, appender: A, level: L, ) -> Builder { let appender = appender.into(); let level = level.into(); - if appender.is_some() || level.is_some() { - self.filters.push(Directive { - path: module_path, - appender: appender, - level: level, - }); + match (appender, level) { + (Some(appender), Some(lvl)) => self.filter_with( + move |_msg, level, target| module_path == target && level == lvl, + appender, + ), + (Some(appender), None) => { + self.filter_with(move |_msg, _lvl, target| module_path == target, appender) + } + + _ => self, } - self } /// Add a filter to redirect log to different output @@ -930,7 +939,11 @@ impl Builder { /// If we configure `max_log_level` to INFO, and even if filter's level is set to DEBUG, /// ftlog will still log up to INFO. #[inline] - pub fn filter_with>, F>(mut self, filter: F, appender: A) -> Builder + pub fn filter_with>, F>( + mut self, + filter: F, + appender: A, + ) -> Builder where F: Fn(&dyn Display, Level, &str) -> bool + Send + Sync + 'static, { diff --git a/tests/local_tz.rs b/tests/local_tz.rs index 02b45e2..7a6b5c7 100644 --- a/tests/local_tz.rs +++ b/tests/local_tz.rs @@ -5,13 +5,29 @@ use std::{ use ftlog::appender::{Duration, FileAppender, Period}; -pub fn setup() { +pub fn setup_with_closures() { let logger = ftlog::Builder::new() .bounded(10000, true) .root(FileAppender::new("./root.log")) - .filter(|_msg, _level, target| target == "rotate", "rotate") + .filter_with(|_msg, _level, target| target == "rotate", "rotate") .appender("rotate", FileAppender::rotate("rotate.log", Period::Minute)) - .filter(|_msg, _level, target| target == "expire", "expire") + .filter_with(|_msg, _level, target| target == "expire", "expire") + .appender( + "expire", + FileAppender::rotate_with_expire("expire.log", Period::Day, Duration::days(7)), + ) + .build() + .expect("logger build failed"); + logger.init().expect("set logger failed"); +} +pub fn setup_with_filter() { + let logger = ftlog::Builder::new() + .bounded(10000, true) + .root(FileAppender::new("./root.log")) + // .utc() + .filter("rotate", "rotate", None) + .appender("rotate", FileAppender::rotate("rotate.log", Period::Minute)) + .filter("expire", "expire", None) .appender( "expire", FileAppender::rotate_with_expire("expire.log", Period::Day, Duration::days(7)), @@ -39,9 +55,57 @@ fn clean(dir: &str) { } } #[test] -fn test_speed() { +fn test_speed_local() { + // ~80MB + setup_with_filter(); + let elapsed1 = { + // file + let now = Instant::now(); + for i in 1..=1_000_000 { + ftlog::info!("file log {}", i); + } + ftlog::logger().flush(); + let elapsed = now.elapsed(); + println!("File elapsed: {}s", elapsed.as_secs_f64()); + elapsed + }; + + let elapsed2 = { + // file with rotate + let now = Instant::now(); + for i in 1..=1_000_000 { + ftlog::info!(target:"rotate", "file log {}", i); + } + ftlog::logger().flush(); + let elapsed = now.elapsed(); + println!("Rotate file elapsed: {}s", elapsed.as_secs_f64()); + elapsed + }; + + let elapsed3 = { + // file with rotate with expire + let now = Instant::now(); + for i in 1..=1_000_000 { + ftlog::info!(target:"expire", "file log {}", i); + } + ftlog::logger().flush(); + let elapsed = now.elapsed(); + println!( + "Rotate file with expire elapsed: {}s", + elapsed.as_secs_f64() + ); + elapsed + }; + clean("./"); + assert!(elapsed1.as_secs() < 8); + assert!(elapsed2.as_secs() < 8); + assert!(elapsed3.as_secs() < 8); +} + +#[test] +fn test_speed_local2() { // ~80MB - setup(); + setup_with_closures(); let elapsed1 = { // file let now = Instant::now(); diff --git a/tests/utc.rs b/tests/utc.rs index a4f6804..3bcbcae 100644 --- a/tests/utc.rs +++ b/tests/utc.rs @@ -10,9 +10,9 @@ pub fn setup() { .bounded(10000, true) .root(FileAppender::new("./root.log")) // .utc() - .filter(|_msg, _level, target| target == "rotate", "rotate") + .filter_with(|_msg, _level, target| target == "rotate", "rotate") .appender("rotate", FileAppender::rotate("rotate.log", Period::Minute)) - .filter(|_msg, _level, target| target == "expire", "expire") + .filter_with(|_msg, _level, target| target == "expire", "expire") .appender( "expire", FileAppender::rotate_with_expire("expire.log", Period::Day, Duration::days(7)), @@ -40,7 +40,7 @@ fn clean(dir: &str) { } } #[test] -fn test_speed() { +fn test_speed_utc() { // ~80MB setup(); let elapsed1 = {