fix: prune/retier no longer treat a foreign same-stem file as a rolled log
prune() and retier() matched any file starting with "<stem>." (minus the live <stem>.log), with no check that the name actually has this lib's own rolled shape (<stem>.<stamp>[.N].log[.gz]). Since v0.5.0 the default history_stem is the cwd basename, so a project dropping its own same-stem file into log_dir (e.g. proj.audit.log, proj.stats.json) got silently deleted by prune once it aged past backup_count, or folded into retier's tier accounting (gzipped or deleted as if it were a real roll). Candidates are now filtered through _is_rolled_name before being treated as a roll. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
@@ -15,6 +15,7 @@ plain copy.
|
||||
|
||||
import gzip
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import time
|
||||
from typing import Callable, Optional, Tuple
|
||||
@@ -252,12 +253,17 @@ def retier(log_dir: str, stem: str, keep_uncompressed: int, keep_compressed: int
|
||||
ordering is by mtime, then by the roll counter parsed from the name, so a
|
||||
same-second burst (tied mtimes, counter-disambiguated stamps like run.<t>.log /
|
||||
run.<t>.1.log) still tiers newest-first rather than falling back to listdir order.
|
||||
|
||||
candidates are also checked against this lib's own rolled-file shape
|
||||
(`<stem>.<stamp>[.N].log[.gz]`, see `_is_rolled_name`) - a foreign file that only
|
||||
shares the `<stem>.` prefix (e.g. a project's own `<stem>.audit.log` dropped into
|
||||
the same log_dir) is left alone rather than tiered/gzipped/deleted.
|
||||
"""
|
||||
stem = os.path.basename(stem)
|
||||
try:
|
||||
names = [
|
||||
name for name in os.listdir(log_dir)
|
||||
if name.startswith(f"{stem}.") and name != f"{stem}.log"
|
||||
if name != f"{stem}.log" and _is_rolled_name(stem, name)
|
||||
]
|
||||
except OSError:
|
||||
return
|
||||
@@ -305,6 +311,23 @@ def retier(log_dir: str, stem: str, keep_uncompressed: int, keep_compressed: int
|
||||
pass
|
||||
|
||||
|
||||
def _rolled_name_pattern(stem: str) -> "re.Pattern":
|
||||
"""compiled regex matching this lib's own rolled-file shape for stem
|
||||
|
||||
`<stem>.<Y-m-d_H-M-S>[.N].log[.gz]` - the uniform namer output from
|
||||
make_history_namer/rotate_on_start (see attach_rolling). used to filter
|
||||
prune/retier candidates so a foreign file merely sharing the `<stem>.` prefix
|
||||
(e.g. a project's own `proj.audit.log` sitting beside its `proj.<stamp>.log`
|
||||
rolls) is never mistaken for a roll and pruned/retiered/deleted.
|
||||
"""
|
||||
return re.compile(rf"^{re.escape(stem)}\.\d{{4}}-\d{{2}}-\d{{2}}_\d{{2}}-\d{{2}}-\d{{2}}(?:\.\d+)?\.log(?:\.gz)?$")
|
||||
|
||||
|
||||
def _is_rolled_name(stem: str, name: str) -> bool:
|
||||
"""return whether name has this lib's own rolled-log shape for stem"""
|
||||
return _rolled_name_pattern(stem).match(name) is not None
|
||||
|
||||
|
||||
def _roll_counter(path: str) -> int:
|
||||
"""parse the same-second disambiguation counter out of a rolled filename
|
||||
|
||||
@@ -331,6 +354,11 @@ def prune(log_dir: str, stem: str, backup_count: int) -> None:
|
||||
FOOTGUN: `stem` is reduced to its basename so a `name` containing a directory (e.g.
|
||||
"sub/run") still matches the basenamed rolled files in log_dir - else nothing
|
||||
matches and old files pile up forever.
|
||||
|
||||
candidates are also checked against this lib's own rolled-file shape
|
||||
(`<stem>.<stamp>[.N].log[.gz]`, see `_is_rolled_name`) - a foreign file that only
|
||||
shares the `<stem>.` prefix (e.g. a project's own `<stem>.audit.log` dropped into
|
||||
the same log_dir) is left alone rather than counted and deleted as a roll.
|
||||
"""
|
||||
if backup_count <= 0:
|
||||
return
|
||||
@@ -339,7 +367,7 @@ def prune(log_dir: str, stem: str, backup_count: int) -> None:
|
||||
entries = [
|
||||
os.path.join(log_dir, name)
|
||||
for name in os.listdir(log_dir)
|
||||
if name.startswith(f"{stem}.") and name != f"{stem}.log"
|
||||
if name != f"{stem}.log" and _is_rolled_name(stem, name)
|
||||
]
|
||||
except OSError:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user