diff --git a/Cargo.toml b/Cargo.toml index 5d467c2..d4efe01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "file-rotate" -version = "0.5.3" +version = "0.6.0" authors = ["Kevin Robert Stravers ", "Erlend Langseth <3rlendhl@gmail.com>"] edition = "2018" description = "Log rotation for files" diff --git a/README.md b/README.md index 1aeac63..f6cdf67 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ Following are some supplementary examples to get started. ## Basic example ```rust -use file_rotate::{FileRotate, ContentLimit, suffix::CountSuffix}; +use file_rotate::{FileRotate, ContentLimit, suffix::AppendCount}; use std::{fs, io::Write, path::PathBuf}; fn main() { - let mut log = FileRotate::new("logs/log", CountSuffix::new(2), ContentLimit::Lines(3)); + let mut log = FileRotate::new("logs/log", AppendCount::new(2), ContentLimit::Lines(3)); // Write a bunch of lines writeln!(log, "Line 1: Hello World!"); @@ -46,7 +46,7 @@ Line 10 ```rust let mut log = FileRotate::new( "logs/log", - TimestampSuffix::default(FileLimit::MaxFiles(3)), + AppendTimestamp::default(FileLimit::MaxFiles(3)), ContentLimit::Lines(3), ); diff --git a/src/compression.rs b/src/compression.rs index 43bf536..70095ed 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -1,3 +1,4 @@ +//! Compression - configuration and implementation use flate2::write::GzEncoder; use std::{ fs::{self, File, OpenOptions}, diff --git a/src/lib.rs b/src/lib.rs index 009de26..10c95ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! We can rotate log files with the amount of lines as a limit, by using [ContentLimit::Lines]. //! //! ``` -//! use file_rotate::{FileRotate, ContentLimit, suffix::CountSuffix, compression::Compression}; +//! use file_rotate::{FileRotate, ContentLimit, suffix::AppendCount, compression::Compression}; //! use std::{fs, io::Write}; //! //! // Create a new log writer. The first argument is anything resembling a path. The @@ -24,7 +24,12 @@ //! # let directory = directory.path(); //! let log_path = directory.join("my-log-file"); //! -//! let mut log = FileRotate::new(log_path.clone(), CountSuffix::new(2), ContentLimit::Lines(3), Compression::None); +//! let mut log = FileRotate::new( +//! log_path.clone(), +//! AppendCount::new(2), +//! ContentLimit::Lines(3), +//! Compression::None +//! ); //! //! // Write a bunch of lines //! writeln!(log, "Line 1: Hello World!"); @@ -43,14 +48,19 @@ //! Another method of rotation is by bytes instead of lines, with [ContentLimit::Bytes]. //! //! ``` -//! use file_rotate::{FileRotate, ContentLimit, suffix::CountSuffix, compression::Compression}; +//! use file_rotate::{FileRotate, ContentLimit, suffix::AppendCount, compression::Compression}; //! use std::{fs, io::Write}; //! //! # let directory = tempdir::TempDir::new("rotation-doc-test").unwrap(); //! # let directory = directory.path(); //! let log_path = directory.join("my-log-file"); //! -//! let mut log = FileRotate::new("target/my-log-directory-bytes/my-log-file", CountSuffix::new(2), ContentLimit::Bytes(5), Compression::None); +//! let mut log = FileRotate::new( +//! "target/my-log-directory-bytes/my-log-file", +//! AppendCount::new(2), +//! ContentLimit::Bytes(5), +//! Compression::None +//! ); //! //! writeln!(log, "Test file"); //! @@ -67,20 +77,25 @@ //! //! ## Basic count ## //! -//! With [CountSuffix], when the limit is reached in the main log file, the file is moved with +//! With [AppendCount], when the limit is reached in the main log file, the file is moved with //! suffix `.1`, and subsequently numbered files are moved in a cascade. //! //! Here's an example with 1 byte limits: //! //! ``` -//! use file_rotate::{FileRotate, ContentLimit, suffix::CountSuffix, compression::Compression}; +//! use file_rotate::{FileRotate, ContentLimit, suffix::AppendCount, compression::Compression}; //! use std::{fs, io::Write}; //! //! # let directory = tempdir::TempDir::new("rotation-doc-test").unwrap(); //! # let directory = directory.path(); //! let log_path = directory.join("my-log-file"); //! -//! let mut log = FileRotate::new(log_path.clone(), CountSuffix::new(3), ContentLimit::Bytes(1), Compression::None); +//! let mut log = FileRotate::new( +//! log_path.clone(), +//! AppendCount::new(3), +//! ContentLimit::Bytes(1), +//! Compression::None +//! ); //! //! write!(log, "A"); //! assert_eq!("A", fs::read_to_string(&log_path).unwrap()); @@ -109,12 +124,12 @@ //! //! ## Timestamp suffix ## //! -//! With [TimestampSuffixScheme], when the limit is reached in the main log file, the file is moved with +//! With [AppendTimestamp], when the limit is reached in the main log file, the file is moved with //! suffix equal to the current timestamp (with the specified or a default format). If the //! destination file name already exists, `.1` (and up) is appended. //! -//! Note that this works somewhat different to `CountSuffix` because of lexical ordering concerns: -//! Higher numbers mean more recent logs, whereas `CountSuffix` works in the opposite way. +//! Note that this works somewhat different to `AppendCount` because of lexical ordering concerns: +//! Higher numbers mean more recent logs, whereas `AppendCount` works in the opposite way. //! The reason for this is to keep the lexical ordering of log names consistent: Higher lexical value //! means more recent. //! This is of course all assuming that the format start with the year (or most significant @@ -124,7 +139,7 @@ //! their timestamp ([FileLimit::Age]), or just maximum number of files ([FileLimit::MaxFiles]). //! //! ``` -//! use file_rotate::{FileRotate, ContentLimit, suffix::{TimestampSuffixScheme, FileLimit}, +//! use file_rotate::{FileRotate, ContentLimit, suffix::{AppendTimestamp, FileLimit}, //! compression::Compression}; //! use std::{fs, io::Write}; //! @@ -132,7 +147,12 @@ //! # let directory = directory.path(); //! let log_path = directory.join("my-log-file"); //! -//! let mut log = FileRotate::new(log_path.clone(), TimestampSuffixScheme::default(FileLimit::MaxFiles(2)), ContentLimit::Bytes(1), Compression::None); +//! let mut log = FileRotate::new( +//! log_path.clone(), +//! AppendTimestamp::default(FileLimit::MaxFiles(2)), +//! ContentLimit::Bytes(1), +//! Compression::None +//! ); //! //! write!(log, "A"); //! assert_eq!("A", fs::read_to_string(&log_path).unwrap()); @@ -155,8 +175,8 @@ //! If you use timestamps as suffix, you can also configure files to be removed as they reach a //! certain age. For example: //! ```rust -//! use file_rotate::suffix::{TimestampSuffixScheme, FileLimit}; -//! TimestampSuffixScheme::default(FileLimit::Age(chrono::Duration::weeks(1))); +//! use file_rotate::suffix::{AppendTimestamp, FileLimit}; +//! AppendTimestamp::default(FileLimit::Age(chrono::Duration::weeks(1))); //! ``` //! //! # Compression # @@ -173,7 +193,7 @@ //! //! let mut log = FileRotate::new( //! "./log", -//! TimestampSuffixScheme::default(FileLimit::MaxFiles(4)), +//! AppendTimestamp::default(FileLimit::MaxFiles(4)), //! ContentLimit::Bytes(1), //! Compression::OnRotate(2), //! ); @@ -204,7 +224,7 @@ //! # use std::path::Path; //! println!( //! "{:#?}", -//! TimestampSuffixScheme::default(FileLimit::MaxFiles(4)).scan_suffixes(Path::new("./log")) +//! AppendTimestamp::default(FileLimit::MaxFiles(4)).scan_suffixes(Path::new("./log")) //! ); //! ``` //! @@ -272,9 +292,7 @@ use std::{ }; use suffix::*; -/// Compression pub mod compression; -/// Suffix scheme etc pub mod suffix; #[cfg(test)] mod tests; @@ -443,7 +461,7 @@ impl FileRotate { &mut self, old_suffix_info: Option>, ) -> io::Result> { - // NOTE: this newest_suffix is there only because TimestampSuffixScheme specifically needs + // NOTE: this newest_suffix is there only because AppendTimestamp specifically needs // it. Otherwise it might not be necessary to provide this to `rotate_file`. We could also // have passed the internal BTreeMap itself, but it would require to make SuffixInfo `pub`. diff --git a/src/suffix.rs b/src/suffix.rs index 644a1b9..98c3101 100644 --- a/src/suffix.rs +++ b/src/suffix.rs @@ -1,3 +1,8 @@ +//! Suffix schemes determine the suffix of rotated files +//! +//! This behaviour is fully extensible through the [SuffixScheme] trait, and two behaviours are +//! provided: [AppendCount] and [AppendTimestamp] +//! use crate::SuffixInfo; #[cfg(feature = "chrono04")] use chrono::{offset::Local, Duration, NaiveDateTime}; @@ -97,15 +102,16 @@ fn prepare_filename(path: &str) -> (&str, bool) { .unwrap_or((path, false)) } -/// Rotated log files get a number as suffix. The greater the number, the older. The oldest files -/// are deleted. -pub struct CountSuffix { +/// Append a number when rotating the file. +/// The greater the number, the older. The oldest files are deleted. +pub struct AppendCount { max_files: usize, } -impl CountSuffix { - /// New suffix scheme, deleting files when the total number of files exceeds `max_files`. - /// For example, if max_files is 3, then the files `log`, `log.1`, `log.2`, `log.3` may exist +impl AppendCount { + /// New suffix scheme, deleting files when the number of rotated files (i.e. excluding the main + /// file) exceeds `max_files`. + /// For example, if `max_files` is 3, then the files `log`, `log.1`, `log.2`, `log.3` may exist /// but not `log.4`. In other words, `max_files` determines the largest possible suffix number. pub fn new(max_files: usize) -> Self { Self { max_files } @@ -113,7 +119,7 @@ impl CountSuffix { } impl Representation for usize {} -impl SuffixScheme for CountSuffix { +impl SuffixScheme for AppendCount { type Repr = usize; fn rotate_file( &mut self, @@ -134,11 +140,14 @@ impl SuffixScheme for CountSuffix { } } +/// Append current timestamp as suffix when rotating files. +/// If the timestamp already exists, an additional number is appended. +/// /// Current limitations: -/// - Neither `format` or the base filename can include the character `"."`. +/// - Neither `format` nor the base filename can include the character `"."`. /// - The `format` should ensure that the lexical and chronological orderings are the same #[cfg(feature = "chrono04")] -pub struct TimestampSuffixScheme { +pub struct AppendTimestamp { /// The format of the timestamp suffix pub format: &'static str, /// The file limit, e.g. when to delete an old file - by age (given by suffix) or by number of files @@ -146,7 +155,7 @@ pub struct TimestampSuffixScheme { } #[cfg(feature = "chrono04")] -impl TimestampSuffixScheme { +impl AppendTimestamp { /// With format `"%Y%m%dT%H%M%S"` pub fn default(file_limit: FileLimit) -> Self { Self { @@ -154,13 +163,13 @@ impl TimestampSuffixScheme { file_limit, } } - /// Create new TimestampSuffixScheme suffix scheme + /// Create new AppendTimestamp suffix scheme pub fn with_format(format: &'static str, file_limit: FileLimit) -> Self { Self { format, file_limit } } } -/// Structured representation of the suffixes of TimestampSuffixScheme. +/// Structured representation of the suffixes of AppendTimestamp. #[derive(Debug, Clone, PartialEq, Eq)] pub struct TimestampSuffix { /// The timestamp @@ -194,7 +203,7 @@ impl std::fmt::Display for TimestampSuffix { } #[cfg(feature = "chrono04")] -impl SuffixScheme for TimestampSuffixScheme { +impl SuffixScheme for AppendTimestamp { type Repr = TimestampSuffix; fn rotate_file( @@ -257,13 +266,12 @@ impl SuffixScheme for TimestampSuffixScheme { } } -/// How to determine if a file should be deleted, in the case of [TimestampSuffixScheme]. +/// How to determine whether a file should be deleted, in the case of [AppendTimestamp]. #[cfg(feature = "chrono04")] pub enum FileLimit { /// Delete the oldest files if number of files is too high MaxFiles(usize), - /// Delete files whose by their age, determined by the suffix (only works in the case that - /// [TimestampSuffixScheme] is used) + /// Delete files whose age exceeds the `Duration` - age is determined by the suffix of the file Age(Duration), } @@ -297,7 +305,7 @@ mod test { fn scan_suffixes() { let working_dir = tempdir::TempDir::new("file-rotate").unwrap(); let working_dir = working_dir.path().join("dir"); - let suffix_scheme = TimestampSuffixScheme::default(FileLimit::Age(Duration::weeks(1))); + let suffix_scheme = AppendTimestamp::default(FileLimit::Age(Duration::weeks(1))); // Test `scan_suffixes` for different possible paths given to it // (it used to have a bug taking e.g. "log".parent() --> panic) diff --git a/src/tests.rs b/src/tests.rs index c855a25..38207c4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -23,7 +23,7 @@ fn timestamp_max_files_rotation() { let mut log = FileRotate::new( &log_path, - TimestampSuffixScheme::default(FileLimit::MaxFiles(4)), + AppendTimestamp::default(FileLimit::MaxFiles(4)), ContentLimit::Lines(2), Compression::None, ); @@ -80,7 +80,7 @@ fn timestamp_max_age_deletion() { let mut log = FileRotate::new( &*log_path.to_string_lossy(), - TimestampSuffixScheme::default(FileLimit::Age(chrono::Duration::weeks(1))), + AppendTimestamp::default(FileLimit::Age(chrono::Duration::weeks(1))), ContentLimit::Lines(1), Compression::None, ); @@ -105,7 +105,7 @@ fn count_max_files_rotation() { let log_path = parent.join("log"); let mut log = FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(4), + AppendCount::new(4), ContentLimit::Lines(2), Compression::None, ); @@ -145,7 +145,7 @@ fn rotate_to_deleted_directory() { let log_path = parent.join("log"); let mut log = FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(4), + AppendCount::new(4), ContentLimit::Lines(1), Compression::None, ); @@ -174,7 +174,7 @@ fn write_complete_record_until_bytes_surpassed() { let mut log = FileRotate::new( &log_path, - TimestampSuffixScheme::default(FileLimit::MaxFiles(100)), + AppendTimestamp::default(FileLimit::MaxFiles(100)), ContentLimit::BytesSurpassed(1), Compression::None, ); @@ -198,7 +198,7 @@ fn compression_on_rotation() { let log_path = parent.join("log"); let mut log = FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(3), + AppendCount::new(3), ContentLimit::Lines(1), Compression::OnRotate(1), // Keep one file uncompressed ); @@ -241,7 +241,7 @@ fn no_truncate() { let file_rotate = || { FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(3), + AppendCount::new(3), ContentLimit::Lines(10000), Compression::None, ) @@ -266,7 +266,7 @@ fn byte_count_recalculation() { let mut file_rotate = FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(3), + AppendCount::new(3), ContentLimit::Bytes(2), Compression::None, ); @@ -293,7 +293,7 @@ fn line_count_recalculation() { let mut file_rotate = FileRotate::new( &*log_path.to_string_lossy(), - CountSuffix::new(3), + AppendCount::new(3), ContentLimit::Lines(2), Compression::None, ); @@ -325,7 +325,7 @@ fn arbitrary_lines(count: usize) { let count = count.max(1); let mut log = FileRotate::new( &log_path, - TimestampSuffixScheme::default(FileLimit::MaxFiles(100)), + AppendTimestamp::default(FileLimit::MaxFiles(100)), ContentLimit::Lines(count), Compression::None, ); @@ -349,7 +349,7 @@ fn arbitrary_bytes(count: usize) { let count = count.max(1); let mut log = FileRotate::new( &log_path, - TimestampSuffixScheme::default(FileLimit::MaxFiles(100)), + AppendTimestamp::default(FileLimit::MaxFiles(100)), ContentLimit::Bytes(count), Compression::None, );