-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add default features for time and compression #28
Open
zkxs
wants to merge
2
commits into
kstrafe:master
Choose a base branch
from
zkxs:time-and-compression-features
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -291,7 +291,9 @@ | |
unused_qualifications | ||
)] | ||
|
||
#[cfg(feature = "time")] | ||
use chrono::prelude::*; | ||
#[cfg(feature = "compression")] | ||
use compression::*; | ||
use std::io::{BufRead, BufReader}; | ||
use std::{ | ||
|
@@ -306,6 +308,7 @@ use suffix::*; | |
#[cfg(unix)] | ||
use std::os::unix::fs::OpenOptionsExt; | ||
|
||
#[cfg(feature = "compression")] | ||
pub mod compression; | ||
pub mod suffix; | ||
#[cfg(test)] | ||
|
@@ -315,6 +318,7 @@ mod tests; | |
|
||
/// At which frequency to rotate the file. | ||
#[derive(Clone, Copy, Debug)] | ||
#[cfg(feature = "time")] | ||
pub enum TimeFrequency { | ||
/// Rotate every hour. | ||
Hourly, | ||
|
@@ -336,6 +340,7 @@ pub enum ContentLimit { | |
/// Cut the log file at line breaks. | ||
Lines(usize), | ||
/// Cut the log at time interval. | ||
#[cfg(feature = "time")] | ||
Time(TimeFrequency), | ||
/// Cut the log file after surpassing size in bytes (but having written a complete buffer from a write call.) | ||
BytesSurpassed(usize), | ||
|
@@ -385,15 +390,14 @@ impl<Repr: Representation> PartialOrd for SuffixInfo<Repr> { | |
pub struct FileRotate<S: SuffixScheme> { | ||
basepath: PathBuf, | ||
file: Option<File>, | ||
modified: Option<DateTime<Local>>, | ||
#[cfg(feature = "time")] modified: Option<DateTime<Local>>, | ||
content_limit: ContentLimit, | ||
count: usize, | ||
compression: Compression, | ||
#[cfg(feature = "compression")] compression: Compression, | ||
suffix_scheme: S, | ||
/// The bool is whether or not there's a .gz suffix to the filename | ||
suffixes: BTreeSet<SuffixInfo<S::Repr>>, | ||
#[cfg(unix)] | ||
mode: Option<u32>, | ||
#[cfg(unix)] mode: Option<u32>, | ||
} | ||
|
||
impl<S: SuffixScheme> FileRotate<S> { | ||
|
@@ -411,7 +415,7 @@ impl<S: SuffixScheme> FileRotate<S> { | |
path: P, | ||
suffix_scheme: S, | ||
content_limit: ContentLimit, | ||
compression: Compression, | ||
#[cfg(feature = "compression")] compression: Compression, | ||
#[cfg(unix)] mode: Option<u32>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated to this commit, but this code forces cross-platform code that uses this library to specify |
||
) -> Self { | ||
match content_limit { | ||
|
@@ -421,7 +425,7 @@ impl<S: SuffixScheme> FileRotate<S> { | |
ContentLimit::Lines(lines) => { | ||
assert!(lines > 0); | ||
} | ||
ContentLimit::Time(_) => {} | ||
#[cfg(feature = "time")] ContentLimit::Time(_) => {} | ||
ContentLimit::BytesSurpassed(bytes) => { | ||
assert!(bytes > 0); | ||
} | ||
|
@@ -433,15 +437,14 @@ impl<S: SuffixScheme> FileRotate<S> { | |
|
||
let mut s = Self { | ||
file: None, | ||
modified: None, | ||
#[cfg(feature = "time")] modified: None, | ||
basepath, | ||
content_limit, | ||
count: 0, | ||
compression, | ||
#[cfg(feature = "compression")] compression, | ||
suffixes: BTreeSet::new(), | ||
suffix_scheme, | ||
#[cfg(unix)] | ||
mode, | ||
#[cfg(unix)] mode, | ||
}; | ||
s.ensure_log_directory_exists(); | ||
s.scan_suffixes(); | ||
|
@@ -473,6 +476,7 @@ impl<S: SuffixScheme> FileRotate<S> { | |
ContentLimit::Lines(_) => { | ||
self.count = BufReader::new(file).lines().count(); | ||
} | ||
#[cfg(feature = "time")] | ||
ContentLimit::Time(_) => { | ||
self.modified = mtime(file); | ||
} | ||
|
@@ -607,6 +611,7 @@ impl<S: SuffixScheme> FileRotate<S> { | |
} | ||
|
||
// Compression | ||
#[cfg(feature = "compression")] | ||
if let Compression::OnRotate(max_file_n) = self.compression { | ||
let n = (self.suffixes.len() as i32 - max_file_n as i32).max(0) as usize; | ||
// The oldest N files should be compressed | ||
|
@@ -652,6 +657,7 @@ impl<S: SuffixScheme> Write for FileRotate<S> { | |
file.write_all(buf)?; | ||
} | ||
} | ||
#[cfg(feature = "time")] | ||
ContentLimit::Time(time) => { | ||
let local: DateTime<Local> = now(); | ||
|
||
|
@@ -741,7 +747,7 @@ impl<S: SuffixScheme> Write for FileRotate<S> { | |
} | ||
|
||
/// Get modification time, in non test case. | ||
#[cfg(not(test))] | ||
#[cfg(all(not(test), feature = "time"))] | ||
fn mtime(file: &File) -> Option<DateTime<Local>> { | ||
if let Ok(time) = file.metadata().and_then(|metadata| metadata.modified()) { | ||
return Some(time.into()); | ||
|
@@ -751,19 +757,19 @@ fn mtime(file: &File) -> Option<DateTime<Local>> { | |
} | ||
|
||
/// Get modification time, in test case. | ||
#[cfg(test)] | ||
#[cfg(all(test, feature = "time"))] | ||
fn mtime(_: &File) -> Option<DateTime<Local>> { | ||
Some(now()) | ||
} | ||
|
||
/// Get system time, in non test case. | ||
#[cfg(not(test))] | ||
#[cfg(all(not(test), feature = "time"))] | ||
fn now() -> DateTime<Local> { | ||
Local::now() | ||
} | ||
|
||
/// Get mocked system time, in test case. | ||
#[cfg(test)] | ||
#[cfg(all(test, feature = "time"))] | ||
pub mod mock_time { | ||
use super::*; | ||
use std::cell::RefCell; | ||
|
@@ -781,5 +787,5 @@ pub mod mock_time { | |
} | ||
} | ||
|
||
#[cfg(test)] | ||
#[cfg(all(test, feature = "time"))] | ||
pub use mock_time::now; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a fan of making the API contingent upon features; it makes swapping between features in a project using this library harder because the user will need to modify code each time. I suggest we instead panic if this argument is something that's not
Compression::None
when compression is disabled.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that feature-gating args out of a parameter list is unclean. However, I'm not a fan of API that looks correct and smells correct but panics at runtime; that seems like it would just move the developer pain from compile-time to runtime and be more difficult to catch.
Some other ideas:
use
on the extension trait. I've seen other popular libraries do this: as an arbitrary example, here's a trait of mac-specific builder functions that winit only implements for that specific platform.Also, I'd hate to make you feel pressured to merge a PR you dislike so please don't feel like you need to merge this now just to clean it up later. If there's a specific way you want to see this implemented I'm happy to alter the PR. Or if this isn't something you want to consider right now that's also fine: I'm not in any rush.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No-op sounds like a good solution. Ideally with a warning message printed at runtime (or compile time, if possible, but no compile/runtime errors) so we don't get bug reports here and users can easily identify what's wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me present an argument for panicking. The panic will happen only on creation of FileRotate, and I can only really see this object being constructed in the initialization (or very early) in the application when it runs. So any simple test of the application should show this - manual or automatic integration tests.
Here is one example from a popular library, not really of a panic but of silently leaving an application non-functioning due to a programmer error: If you use
actix-web
to build a web server, and you make a mistake in a request handler's parameters asking for app data of a type that has not been registered, it will not warn you at compile time... but your API will always return some5xx
http error saying that it's wrongly configured. Has it ever been a problem for me as a user of actix-web? No, because it makes all my tests fail and it's easily fixed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, a builder pattern sounds like a good solution as well. And it doesn't have to be through an extension trait IMO but maybe just a feature gated function on the builder? I agree that changing the number of parameters with
#[cfg]
can lead to confusion, but what do you think about feature gating builder functions, @kstrafe ?