From 065e2ac007ad91d528cae7823b089ea0f295bea6 Mon Sep 17 00:00:00 2001 From: emabee Date: Fri, 7 Aug 2020 16:57:00 +0200 Subject: [PATCH] [0.15.11] Introduce feature `specfile_without_notification` - fixes emabee/flexi_logger#59 --- CHANGELOG.md | 5 ++ Cargo.toml | 5 +- README.md | 8 +++- scripts/qualify.rs | 7 +++ src/flexi_error.rs | 6 +-- src/log_specification.rs | 11 ++--- src/logger.rs | 88 ++++++++++++++++++----------------- src/reconfiguration_handle.rs | 2 +- tests/test_specfile.rs | 55 ++++++++++++++-------- 9 files changed, 112 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae1a328..18afd52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.15.11] - 2020-08-07 + +Introduce feature `specfile_without_notification` to allow coping with OS issues +(see [issue-59](https://github.com/emabee/flexi_logger/issues/59)). + ## [0.15.10] - 2020-07-22 Minor code maintenance. diff --git a/Cargo.toml b/Cargo.toml index 9d59939..507df55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "flexi_logger" -version = "0.15.10" +version = "0.15.11" authors = ["emabee "] edition = "2018" license = "MIT OR Apache-2.0" @@ -22,7 +22,8 @@ all-features = true [features] default = ["colors", "textfilter"] colors = ["atty", "lazy_static","yansi"] -specfile = ["serde","toml","notify", "serde_derive"] +specfile = ["specfile_without_notification","notify"] +specfile_without_notification = ["serde","toml","serde_derive"] syslog_writer = ["libc", "hostname"] ziplogs = ["flate2"] textfilter = ["regex"] diff --git a/README.md b/README.md index b9d3130..c7d4cdb 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,11 @@ The implementation of this feature uses some additional crates that you might not want to depend on with your program if you don't use this functionality. For that reason the feature is not active by default. +### **`specfile_without_notification`** + +Pretty much like `specfile`, except that updates to the file are being ignored. +See [issue-59](https://github.com/emabee/flexi_logger/issues/59) for more details. + ### **`ziplogs`** The `ziplogs` feature adds two options to the `Logger::Cleanup` `enum`, which allow keeping some @@ -179,7 +184,8 @@ Removes the ability to filter logs by text, but also removes the dependency on t ### **`syslog`** -This is still an experimental feature, likely working, but not well tested. Feedback of all kinds is highly appreciated. +This is still an experimental feature, likely working, but not well tested. +Feedback of all kinds is highly appreciated. ## Versions diff --git a/scripts/qualify.rs b/scripts/qualify.rs index 73b3a75..c699c6b 100644 --- a/scripts/qualify.rs +++ b/scripts/qualify.rs @@ -58,6 +58,13 @@ fn main() { run_command!("cargo", "test", "--release", "--all-features"); run_command!("cargo", "test", "--no-default-features"); run_command!("cargo", "test", "--release"); + run_command!( + "cargo", + "test", + "--release", + "--features", + "specfile_without_notification" + ); run_script("cleanup"); // doc diff --git a/src/flexi_error.rs b/src/flexi_error.rs index 63f2425..36ae166 100644 --- a/src/flexi_error.rs +++ b/src/flexi_error.rs @@ -28,17 +28,17 @@ pub enum FlexiLoggerError { /// Parsing the configured logspec toml-file failed. #[error("Parsing the configured logspec toml-file failed")] - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] SpecfileToml(#[from] toml::de::Error), /// Specfile cannot be accessed or created. #[error("Specfile cannot be accessed or created")] - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] SpecfileIo(std::io::Error), /// Specfile has an unsupported extension. #[error("Specfile has an unsupported extension")] - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] SpecfileExtension(&'static str), /// Invalid level filter. diff --git a/src/log_specification.rs b/src/log_specification.rs index b048313..477e436 100644 --- a/src/log_specification.rs +++ b/src/log_specification.rs @@ -246,7 +246,7 @@ impl LogSpecification { /// # Errors /// /// `FlexiLoggerError::Parse` if the input is malformed. - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] pub fn from_toml(s: &str) -> Result { #[derive(Clone, Debug, serde_derive::Deserialize)] struct LogSpecFileFormat { @@ -304,7 +304,7 @@ impl LogSpecification { /// # Errors /// /// `FlexiLoggerError::Io` if writing fails. - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] pub fn to_toml(&self, w: &mut dyn std::io::Write) -> Result<(), FlexiLoggerError> { w.write_all(b"### Optional: Default log level\n")?; let last = self.module_filters.last(); @@ -390,7 +390,6 @@ fn parse_err( Err(FlexiLoggerError::Parse(errors, logspec)) } -// #[cfg(feature = "specfile")] fn parse_level_filter>(s: S) -> Result { match s.as_ref().to_lowercase().as_ref() { "off" => Ok(LevelFilter::Off), @@ -866,9 +865,9 @@ mod tests { } #[cfg(test)] -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] mod test_with_specfile { - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] use crate::LogSpecification; #[test] @@ -909,7 +908,7 @@ mod test_with_specfile { ); } - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] fn compare_specs(s1: &str, s2: &str) { let ls1 = LogSpecification::from_toml(s1).unwrap(); let ls2 = LogSpecification::parse(s2).unwrap(); diff --git a/src/logger.rs b/src/logger.rs index 4306ad1..f524337 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -7,7 +7,7 @@ use crate::{formats, FlexiLoggerError, LogSpecification}; #[cfg(feature = "specfile")] use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use std::collections::HashMap; -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] use std::io::Read; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -697,7 +697,7 @@ impl Logger { /// /// A `ReconfigurationHandle` is returned, predominantly to allow using its /// [`shutdown`](struct.ReconfigurationHandle.html#method.shutdown) method. - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] pub fn start_with_specfile>( self, specfile: P, @@ -727,7 +727,7 @@ impl Logger { /// /// A `ReconfigurationHandle` is returned, predominantly to allow using its /// [`shutdown`](struct.ReconfigurationHandle.html#method.shutdown) method. - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] pub fn build_with_specfile>( self, specfile: P, @@ -738,7 +738,7 @@ impl Logger { } } -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] fn setup_specfile>( specfile: P, mut handle: ReconfigurationHandle, @@ -746,59 +746,63 @@ fn setup_specfile>( let specfile = specfile.as_ref().to_owned(); synchronize_handle_with_specfile(&mut handle, &specfile)?; - // Now that the file exists, we can canonicalize the path - let specfile = specfile - .canonicalize() - .map_err(FlexiLoggerError::SpecfileIo)?; - - // Watch the parent folder of the specfile, using debounced events - let (tx, rx) = std::sync::mpsc::channel(); - let debouncing_delay = std::time::Duration::from_millis(1000); - let mut watcher = watcher(tx, debouncing_delay)?; - watcher.watch(&specfile.parent().unwrap(), RecursiveMode::NonRecursive)?; - - // in a separate thread, reread the specfile when it was updated - std::thread::Builder::new() - .name("flexi_logger-specfile-watcher".to_string()) - .stack_size(128 * 1024) - .spawn(move || { - let _anchor_for_watcher = watcher; // keep it alive! - loop { - match rx.recv() { - Ok(debounced_event) => { - // println!("got debounced event {:?}", debounced_event); - match debounced_event { - DebouncedEvent::Create(ref path) | DebouncedEvent::Write(ref path) => { - if path.canonicalize().map(|x| x == specfile).unwrap_or(false) { - match log_spec_string_from_file(&specfile) - .map_err(FlexiLoggerError::SpecfileIo) - .and_then(|s| LogSpecification::from_toml(&s)) - { - Ok(spec) => handle.set_new_spec(spec), - Err(e) => eprintln!( + #[cfg(feature = "specfile")] + { + // Now that the file exists, we can canonicalize the path + let specfile = specfile + .canonicalize() + .map_err(FlexiLoggerError::SpecfileIo)?; + + // Watch the parent folder of the specfile, using debounced events + let (tx, rx) = std::sync::mpsc::channel(); + let debouncing_delay = std::time::Duration::from_millis(1000); + let mut watcher = watcher(tx, debouncing_delay)?; + watcher.watch(&specfile.parent().unwrap(), RecursiveMode::NonRecursive)?; + + // in a separate thread, reread the specfile when it was updated + std::thread::Builder::new() + .name("flexi_logger-specfile-watcher".to_string()) + .stack_size(128 * 1024) + .spawn(move || { + let _anchor_for_watcher = watcher; // keep it alive! + loop { + match rx.recv() { + Ok(debounced_event) => { + // println!("got debounced event {:?}", debounced_event); + match debounced_event { + DebouncedEvent::Create(ref path) + | DebouncedEvent::Write(ref path) => { + if path.canonicalize().map(|x| x == specfile).unwrap_or(false) { + match log_spec_string_from_file(&specfile) + .map_err(FlexiLoggerError::SpecfileIo) + .and_then(|s| LogSpecification::from_toml(&s)) + { + Ok(spec) => handle.set_new_spec(spec), + Err(e) => eprintln!( "[flexi_logger] rereading the log specification file \ failed with {:?}, \ continuing with previous log specification", e ), + } } } + _event => {} } - _event => {} } - } - Err(e) => { - eprintln!("[flexi_logger] error while watching the specfile: {:?}", e) + Err(e) => { + eprintln!("[flexi_logger] error while watching the specfile: {:?}", e) + } } } - } - })?; + })?; + } Ok(()) } // If the specfile exists, read the file and update the log_spec from it; // otherwise try to create the file, with the current spec as content, under the specified name. -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] pub(crate) fn synchronize_handle_with_specfile( handle: &mut ReconfigurationHandle, specfile: &std::path::PathBuf, @@ -840,7 +844,7 @@ pub(crate) fn synchronize_handle_with_specfile( Ok(()) } -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] pub(crate) fn log_spec_string_from_file>( specfile: P, ) -> Result { diff --git a/src/reconfiguration_handle.rs b/src/reconfiguration_handle.rs index 0df2a84..4bc716f 100644 --- a/src/reconfiguration_handle.rs +++ b/src/reconfiguration_handle.rs @@ -70,7 +70,7 @@ impl ReconfigurationHandle { } } - #[cfg(feature = "specfile")] + #[cfg(feature = "specfile_without_notification")] pub(crate) fn current_spec(&self) -> Arc> { Arc::clone(&self.spec) } diff --git a/tests/test_specfile.rs b/tests/test_specfile.rs index 67ca961..7e64b9f 100644 --- a/tests/test_specfile.rs +++ b/tests/test_specfile.rs @@ -1,17 +1,13 @@ -#[cfg(feature = "specfile")] +#[cfg(feature = "specfile_without_notification")] mod a { use flexi_logger::{detailed_format, Logger}; use log::*; use std::io::{BufRead, Write}; use std::ops::Add; - const WAIT: u64 = 2000; + const WAIT_MILLIS: u64 = 2000; - /// Rudimentary test of the specfile feature, using the file ./tests/logspec.toml. - /// For real test, run this manually, change the duration before to a much higher value (see below), - /// and edit the file while the test is running. You should see the impact immediately - - /// by default, ERR, WARN, and INFO messages are printed. If you change the level in the file, - /// less or more lines should be printed. + /// Test of the specfile feature, using the file ./tests/logspec.toml. #[test] fn test_specfile() { let specfile = "test_spec/test_specfile_logspec.toml"; @@ -52,7 +48,7 @@ mod a { .unwrap(); } - std::thread::sleep(std::time::Duration::from_millis(WAIT)); + std::thread::sleep(std::time::Duration::from_millis(WAIT_MILLIS)); error!("This is an error-1"); warn!("This is a warning-1"); @@ -79,7 +75,7 @@ mod a { .unwrap(); } - std::thread::sleep(std::time::Duration::from_millis(WAIT)); + std::thread::sleep(std::time::Duration::from_millis(WAIT_MILLIS)); error!("This is an error-2"); warn!("This is a warning-2"); @@ -94,17 +90,36 @@ mod a { .to_string() .add(".log"); - validate_logs( - &logfile, - &[ - ("ERROR", "test_specfile::a", "error-0"), - ("WARN", "test_specfile::a", "warning-0"), - ("INFO", "test_specfile::a", "info-0"), - ("ERROR", "test_specfile::a", "error-1"), - ("WARN", "test_specfile::a", "warning-1"), - ("ERROR", "test_specfile::a", "error-2"), - ], - ); + if cfg!(feature = "specfile") { + eprintln!("feature is: specfile!"); + validate_logs( + &logfile, + &[ + ("ERROR", "test_specfile::a", "error-0"), + ("WARN", "test_specfile::a", "warning-0"), + ("INFO", "test_specfile::a", "info-0"), + ("ERROR", "test_specfile::a", "error-1"), + ("WARN", "test_specfile::a", "warning-1"), + ("ERROR", "test_specfile::a", "error-2"), + ], + ); + } else { + eprintln!("feature is: specfile_without_notification!"); + validate_logs( + &logfile, + &[ + ("ERROR", "test_specfile::a", "error-0"), + ("WARN", "test_specfile::a", "warning-0"), + ("INFO", "test_specfile::a", "info-0"), + ("ERROR", "test_specfile::a", "error-1"), + ("WARN", "test_specfile::a", "warning-1"), + ("INFO", "test_specfile::a", "info-1"), + ("ERROR", "test_specfile::a", "error-2"), + ("WARN", "test_specfile::a", "warning-2"), + ("INFO", "test_specfile::a", "info-2"), + ], + ); + } } fn validate_logs(logfile: &str, expected: &[(&'static str, &'static str, &'static str)]) {