From 3f3a797fded4a70c501a85150095970fe172d704 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Mon, 6 Jul 2026 17:19:38 -0400 Subject: [PATCH] fix: prune/retier recognize legacy pre-v0.5.0 daily rolls; remove dead make_namer _is_rolled_name only matched the current uniform namer shape, so an upgraded deployment's existing pre-v0.5.0 daily rolls (.log.[.gz], the stdlib TimedRotatingFileHandler shape) were classified foreign and never pruned - piling up forever. the pattern now also matches that legacy shape (still date-bearing, so a foreign .audit.log is untouched). also drops make_namer, which had zero callers since attach_rolling moved to make_history_namer. Signed-off-by: disqualifier --- src/log_setup/rotation.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/log_setup/rotation.py b/src/log_setup/rotation.py index 88f7e69..3a928b1 100644 --- a/src/log_setup/rotation.py +++ b/src/log_setup/rotation.py @@ -99,19 +99,6 @@ def _gz_intact(path: str) -> bool: return False -def make_namer(log_dir: str, compress: bool) -> Callable[[str], str]: - """namer: redirect a rolled filename into log_dir, adding .gz when compressing - - keeps the handler's default rolled basename but places it under log_dir, appending - .gz so the gzipped name matches. - """ - def namer(default_name: str) -> str: - base = os.path.basename(default_name) - target = os.path.join(log_dir, base) - return target + ".gz" if compress else target - return namer - - def make_history_namer( stem: str, log_dir: str, compress: bool = False, plain: bool = False, clock=time.localtime, @@ -311,15 +298,20 @@ def retier(log_dir: str, stem: str, keep_uncompressed: int, keep_compressed: int def _rolled_name_pattern(stem: str) -> "re.Pattern": - """compiled regex matching this lib's own rolled-file shape for stem + """compiled regex matching this lib's own rolled-file shapes for stem - `.[.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 `.` prefix - (e.g. a project's own `proj.audit.log` sitting beside its `proj..log` - rolls) is never mistaken for a roll and pruned/retiered/deleted. + two forms, both date-bearing so a foreign same-stem file (e.g. `proj.audit.log`) is + never mistaken for a roll and pruned/retiered/deleted: + - current uniform namer: `.[.N].log[.gz]` + (make_history_namer/rotate_on_start, see attach_rolling) + - legacy pre-v0.5.0 daily: `.log.[.gz]` (the stdlib TimedRotatingFileHandler + shape) - matched so an upgraded deployment's existing history is still pruned/retired + instead of piling up forever """ - return re.compile(rf"^{re.escape(stem)}\.\d{{4}}-\d{{2}}-\d{{2}}_\d{{2}}-\d{{2}}-\d{{2}}(?:\.\d+)?\.log(?:\.gz)?$") + esc = re.escape(stem) + uniform = rf"{esc}\.\d{{4}}-\d{{2}}-\d{{2}}_\d{{2}}-\d{{2}}-\d{{2}}(?:\.\d+)?\.log(?:\.gz)?" + legacy = rf"{esc}\.log\.\d{{4}}-\d{{2}}-\d{{2}}(?:\.gz)?" + return re.compile(rf"^(?:{uniform}|{legacy})$") def _is_rolled_name(stem: str, name: str) -> bool: