|
|
|
@ -73,6 +73,7 @@ const DEFAULT_LOG_TARGETS: [Target; 2] = [
|
|
|
|
|
Target::new(TargetKind::Stdout),
|
|
|
|
|
Target::new(TargetKind::LogDir { file_name: None }),
|
|
|
|
|
];
|
|
|
|
|
const LOG_DATE_FORMAT: &str = "[year]-[month]-[day]_[hour]-[minute]-[second]";
|
|
|
|
|
|
|
|
|
|
/// An enum representing the available verbosity levels of the logger.
|
|
|
|
|
///
|
|
|
|
@ -127,8 +128,12 @@ impl From<log::Level> for LogLevel {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub enum RotationStrategy {
|
|
|
|
|
// Will keep all the logs, renaming them to include the date
|
|
|
|
|
KeepAll,
|
|
|
|
|
// Will only keep the most recent log up to its maximal size
|
|
|
|
|
KeepOne,
|
|
|
|
|
// Will keep some of the most recent logs, renaming them to include the date.
|
|
|
|
|
KeepSome(usize),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
@ -493,31 +498,19 @@ impl Builder {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_log_file_path(
|
|
|
|
|
|
|
|
|
|
fn rename_file_to_dated(
|
|
|
|
|
path: &impl AsRef<Path>,
|
|
|
|
|
dir: &impl AsRef<Path>,
|
|
|
|
|
file_name: &str,
|
|
|
|
|
rotation_strategy: &RotationStrategy,
|
|
|
|
|
timezone_strategy: &TimezoneStrategy,
|
|
|
|
|
max_file_size: u128,
|
|
|
|
|
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
|
|
|
|
let path = dir.as_ref().join(format!("{file_name}.log"));
|
|
|
|
|
|
|
|
|
|
if path.exists() {
|
|
|
|
|
let log_size = File::open(&path)?.metadata()?.len() as u128;
|
|
|
|
|
if log_size > max_file_size {
|
|
|
|
|
match rotation_strategy {
|
|
|
|
|
RotationStrategy::KeepAll => {
|
|
|
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
|
let to = dir.as_ref().join(format!(
|
|
|
|
|
"{}_{}.log",
|
|
|
|
|
file_name,
|
|
|
|
|
timezone_strategy
|
|
|
|
|
.get_now()
|
|
|
|
|
.format(
|
|
|
|
|
&time::format_description::parse(
|
|
|
|
|
"[year]-[month]-[day]_[hour]-[minute]-[second]"
|
|
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
)
|
|
|
|
|
.format(&time::format_description::parse(LOG_DATE_FORMAT).unwrap())
|
|
|
|
|
.unwrap(),
|
|
|
|
|
));
|
|
|
|
|
if to.is_file() {
|
|
|
|
@ -530,7 +523,53 @@ fn get_log_file_path(
|
|
|
|
|
));
|
|
|
|
|
fs::rename(&to, to_bak)?;
|
|
|
|
|
}
|
|
|
|
|
fs::rename(&path, to)?;
|
|
|
|
|
fs::rename(path, to)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_log_file_path(
|
|
|
|
|
dir: &impl AsRef<Path>,
|
|
|
|
|
file_name: &str,
|
|
|
|
|
rotation_strategy: &RotationStrategy,
|
|
|
|
|
timezone_strategy: &TimezoneStrategy,
|
|
|
|
|
max_file_size: u128,
|
|
|
|
|
) -> Result<PathBuf, Box<dyn std::error::Error>> {
|
|
|
|
|
let path = dir.as_ref().join(format!("{file_name}.log"));
|
|
|
|
|
|
|
|
|
|
if path.exists() {
|
|
|
|
|
let log_size = File::open(&path)?.metadata()?.len() as u128;
|
|
|
|
|
if log_size > max_file_size {
|
|
|
|
|
match rotation_strategy {
|
|
|
|
|
RotationStrategy::KeepAll => {
|
|
|
|
|
rename_file_to_dated(&path, dir, file_name, timezone_strategy)?;
|
|
|
|
|
}
|
|
|
|
|
RotationStrategy::KeepSome(how_many) => {
|
|
|
|
|
let mut files = fs::read_dir(dir)?
|
|
|
|
|
.filter_map(|entry| {
|
|
|
|
|
let entry = entry.ok()?;
|
|
|
|
|
let path = entry.path();
|
|
|
|
|
let old_file_name = path.file_name()?.to_string_lossy().into_owned();
|
|
|
|
|
if old_file_name.starts_with(&file_name) {
|
|
|
|
|
let date =
|
|
|
|
|
old_file_name.strip_prefix(&file_name)?.strip_prefix("_")?.strip_suffix(".log")?;
|
|
|
|
|
Some((path, date.to_string()))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
// Regular sorting, so the oldest files are first. Lexicographical
|
|
|
|
|
// sorting is fine due to the date format.
|
|
|
|
|
files.sort_by(|a, b| a.1.cmp(&b.1));
|
|
|
|
|
// We want to make space for the file we will be soon renaming, AND
|
|
|
|
|
// the file we will be creating. Thus we need to keep how_many - 2 files.
|
|
|
|
|
if files.len() > (*how_many - 2) {
|
|
|
|
|
files.truncate(files.len() + 2 - *how_many);
|
|
|
|
|
for (old_log_path, _) in files {
|
|
|
|
|
fs::remove_file(old_log_path)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
rename_file_to_dated(&path, dir, file_name, timezone_strategy)?;
|
|
|
|
|
}
|
|
|
|
|
RotationStrategy::KeepOne => {
|
|
|
|
|
fs::remove_file(&path)?;
|
|
|
|
@ -538,6 +577,5 @@ fn get_log_file_path(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(path)
|
|
|
|
|
}
|
|
|
|
|