mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-05-27 13:22:26 +02:00
feat(log): Add KeepSome rotation strategy (#677)
Co-authored-by: Krzysztof Krolak <krzysiek.krolak@gmail.com> Co-authored-by: FabianLars <github@fabianlars.de>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
log: minor
|
||||
log-js: minor
|
||||
---
|
||||
|
||||
Added the `KeepSome` rotation strategy. Like `KeepAll` it will rename files when the max file size is exceeded but will keep only the specified amount of files around.
|
||||
@@ -31,11 +31,15 @@ thiserror = { workspace = true }
|
||||
serde_repr = "0.1"
|
||||
byte-unit = "5"
|
||||
log = { workspace = true, features = ["kv_unstable"] }
|
||||
time = { version = "0.3", features = ["formatting", "local-offset", "macros"] }
|
||||
time = { version = "0.3", features = [
|
||||
"formatting",
|
||||
"local-offset",
|
||||
"macros",
|
||||
"parsing",
|
||||
] }
|
||||
fern = "0.7"
|
||||
tracing = { workspace = true, optional = true }
|
||||
|
||||
|
||||
[target."cfg(target_os = \"android\")".dependencies]
|
||||
android_logger = "0.15"
|
||||
|
||||
|
||||
+63
-21
@@ -47,6 +47,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]";
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@@ -115,8 +116,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)]
|
||||
@@ -577,6 +582,34 @@ pub fn attach_logger(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rename_file_to_dated(
|
||||
path: &impl AsRef<Path>,
|
||||
dir: &impl AsRef<Path>,
|
||||
file_name: &str,
|
||||
timezone_strategy: &TimezoneStrategy,
|
||||
) -> Result<(), Error> {
|
||||
let to = dir.as_ref().join(format!(
|
||||
"{}_{}.log",
|
||||
file_name,
|
||||
timezone_strategy
|
||||
.get_now()
|
||||
.format(&time::format_description::parse(LOG_DATE_FORMAT).unwrap())
|
||||
.unwrap(),
|
||||
));
|
||||
if to.is_file() {
|
||||
// designated rotated log file name already exists
|
||||
// highly unlikely but defensively handle anyway by adding .bak to filename
|
||||
let mut to_bak = to.clone();
|
||||
to_bak.set_file_name(format!(
|
||||
"{}.bak",
|
||||
to_bak.file_name().unwrap().to_string_lossy()
|
||||
));
|
||||
fs::rename(&to, to_bak)?;
|
||||
}
|
||||
fs::rename(path, to)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_log_file_path(
|
||||
dir: &impl AsRef<Path>,
|
||||
file_name: &str,
|
||||
@@ -591,27 +624,37 @@ fn get_log_file_path(
|
||||
if log_size > max_file_size {
|
||||
match rotation_strategy {
|
||||
RotationStrategy::KeepAll => {
|
||||
let to = dir.as_ref().join(format!(
|
||||
"{}_{}.log",
|
||||
file_name,
|
||||
timezone_strategy.get_now().format(&format_description!(
|
||||
"[year]-[month]-[day]_[hour]-[minute]-[second]"
|
||||
))?,
|
||||
));
|
||||
if to.is_file() {
|
||||
// designated rotated log file name already exists
|
||||
// highly unlikely but defensively handle anyway by adding .bak to filename
|
||||
let mut to_bak = to.clone();
|
||||
to_bak.set_file_name(format!(
|
||||
"{}.bak",
|
||||
to_bak
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy())
|
||||
.unwrap_or_default()
|
||||
));
|
||||
fs::rename(&to, to_bak)?;
|
||||
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)?;
|
||||
}
|
||||
}
|
||||
fs::rename(&path, to)?;
|
||||
rename_file_to_dated(&path, dir, file_name, timezone_strategy)?;
|
||||
}
|
||||
RotationStrategy::KeepOne => {
|
||||
fs::remove_file(&path)?;
|
||||
@@ -619,6 +662,5 @@ fn get_log_file_path(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user