Update AzLogs to 2.0.1 and centralize rotated log filename handling

This commit is contained in:
Dariusz L
2026-07-24 22:01:41 +02:00
parent e568f81f2b
commit 21822224b7
6 changed files with 111 additions and 18 deletions
+6 -12
View File
@@ -35,6 +35,7 @@ from .core.file_manager import (
from .core.log_system import LogLevel, create_module_logger
from .core.log_system import logger as backend_log_controller
from .core.log_system.config import LOG_LEVEL as BACKEND_DEFAULT_LOG_LEVEL
from .core.log_system.logger import parse_rotated_log_filename
from .core.path_utils import get_filename_from_path, get_metadata_sidecar_path, get_safe_metadata_sidecar_path
# Web directory for JavaScript interface
@@ -5039,17 +5040,10 @@ class ModelResolverExtension:
def _backend_log_sort_key(path):
name = os.path.basename(path)
rotation = 0
if ".log." in name:
base_name, rotation_text = name.rsplit(".log.", 1)
try:
rotation = int(rotation_text)
except ValueError:
rotation = 999
elif name.endswith(".log"):
base_name = name[:-4]
else:
base_name = name
parsed_name = parse_rotated_log_filename(name)
if parsed_name is None:
return (name.lower(), 999)
base_name, rotation = parsed_name
return (base_name.lower(), rotation)
def _collect_backend_log_files(log_dir):
@@ -5061,7 +5055,7 @@ class ModelResolverExtension:
name = str(entry or "")
if not name.startswith("azlogs_"):
continue
if not (name.endswith(".log") or ".log." in name):
if parse_rotated_log_filename(name) is None:
continue
path = os.path.abspath(os.path.join(log_dir, name))
+4 -3
View File
@@ -1,7 +1,8 @@
"""
AzLogs - Central logging system
Provides colored console logging, file rotation, and per-module log levels.
@author: Azornes
@title: AzLogs
@version: 2.0.1
@description: Logging Initializer
"""
from .log_funcs import create_module_logger
+1 -1
View File
@@ -1,7 +1,7 @@
"""
@author: Azornes
@title: AzLogs
@version: 2.0.0
@version: 2.0.1
@description: Logging Configuration
"""
# Log level for development.
+1 -1
View File
@@ -1,7 +1,7 @@
"""
@author: Azornes
@title: AzLogs
@version: 2.0.0
@version: 2.0.1
@description: Logging Initializer
"""
import logging
+22 -1
View File
@@ -1,7 +1,7 @@
"""
@author: Azornes
@title: AzLogs
@version: 2.0.0
@version: 2.0.1
@description: Logging Setup - Central logging system
Features:
@@ -73,6 +73,26 @@ DEFAULT_CONFIG = {
_WINDOWS_VT_PROCESSING_ENABLED = False
def rotated_log_filename(default_name):
"""Keep the .log extension at the end of rotated log filenames."""
match = re.fullmatch(r"(.+)\.log\.(\d+)", str(default_name or ""))
if not match:
return default_name
return f"{match.group(1)}.{match.group(2)}.log"
def parse_rotated_log_filename(filename):
"""Return the log family and rotation number for the current filename style."""
name = os.path.basename(str(filename or ""))
match = re.fullmatch(r"(.+)\.(\d+)\.log", name)
if match:
return match.group(1), int(match.group(2))
if name.endswith(".log"):
return name[:-4], 0
return None
def _enable_windows_virtual_terminal_processing():
"""Enable ANSI escape sequence handling for classic Windows consoles."""
global _WINDOWS_VT_PROCESSING_ENABLED
@@ -127,6 +147,7 @@ class SafeRotatingFileHandler(RotatingFileHandler):
def __init__(self, *args, **kwargs):
self._disabled_by_stream_error = False
super().__init__(*args, **kwargs)
self.namer = rotated_log_filename
def emit(self, record):
if self._disabled_by_stream_error:
+77
View File
@@ -0,0 +1,77 @@
import logging
import os
import tempfile
import unittest
from core.log_system.logger import (
SafeRotatingFileHandler,
parse_rotated_log_filename,
rotated_log_filename,
)
class RotatedLogFilenameTests(unittest.TestCase):
def test_places_rotation_before_log_extension(self):
path = os.path.join("logs", "azlogs_MResolver.log.3")
self.assertEqual(
rotated_log_filename(path),
os.path.join("logs", "azlogs_MResolver.3.log"),
)
def test_leaves_unrelated_filename_unchanged(self):
path = os.path.join("logs", "azlogs_MResolver.log")
self.assertEqual(rotated_log_filename(path), path)
def test_parser_accepts_current_style(self):
expected = ("azlogs_MResolver", 2)
self.assertEqual(
parse_rotated_log_filename("azlogs_MResolver.2.log"),
expected,
)
self.assertEqual(
parse_rotated_log_filename("azlogs_MResolver.log"),
("azlogs_MResolver", 0),
)
def test_parser_rejects_non_log_and_legacy_files(self):
self.assertIsNone(parse_rotated_log_filename("other.txt"))
self.assertIsNone(parse_rotated_log_filename("azlogs_MResolver.log.2"))
self.assertIsNone(parse_rotated_log_filename("azlogs_MResolver.log2"))
class SafeRotatingFileHandlerTests(unittest.TestCase):
def test_rollover_keeps_log_as_the_final_extension(self):
with tempfile.TemporaryDirectory() as temp_dir:
log_path = os.path.join(temp_dir, "azlogs_MResolver.log")
logger = logging.getLogger(f"test-log-rotation-{id(self)}")
logger.handlers.clear()
logger.propagate = False
logger.setLevel(logging.INFO)
handler = SafeRotatingFileHandler(
log_path,
maxBytes=32,
backupCount=2,
encoding="utf-8",
)
handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(handler)
try:
logger.info("first message large enough to rotate")
logger.info("second message large enough to rotate")
logger.info("third message large enough to rotate")
handler.flush()
finally:
logger.removeHandler(handler)
handler.close()
files = set(os.listdir(temp_dir))
self.assertIn("azlogs_MResolver.log", files)
self.assertIn("azlogs_MResolver.1.log", files)
self.assertIn("azlogs_MResolver.2.log", files)
self.assertNotIn("azlogs_MResolver.3.log", files)
self.assertNotIn("azlogs_MResolver.log.1", files)
if __name__ == "__main__":
unittest.main()