diff --git a/jac-cloud/jac_cloud/jaseci/utils/logger.py b/jac-cloud/jac_cloud/jaseci/utils/logger.py index 23c27c2a71..c8aac1470b 100644 --- a/jac-cloud/jac_cloud/jaseci/utils/logger.py +++ b/jac-cloud/jac_cloud/jaseci/utils/logger.py @@ -3,22 +3,25 @@ from datetime import time as dtime from enum import IntEnum from io import text_encoding -from logging import FileHandler, LogRecord, getLogger +from itertools import chain +from logging import LogRecord, getLogger from logging.handlers import ( BaseRotatingHandler, RotatingFileHandler, TimedRotatingFileHandler, ) -from os import getenv, remove, rename, stat -from os.path import exists, isfile +from os import getenv, remove, stat +from os.path import exists, getmtime, isfile from pathlib import Path -from re import ASCII, compile +from re import ASCII, compile, escape from stat import ST_MTIME from time import gmtime, localtime, strftime, time as ttime from typing import Any from ecs_logging import StdlibFormatter +DEFAULT_PART = [0] + class MixedTimedRotatingFileHandler(TimedRotatingFileHandler, RotatingFileHandler): """Merge TimedRotatingFileHandler and RotatingFileHandler.""" @@ -57,22 +60,26 @@ def __init__( self.backupCount = backup_count self.utc = utc self.atTime = at_time + + re_suffix = escape(self.file_name) + r"-[0-9]+" + escape(self.file_ext) + "$" if self.when == "S": self.interval = 1 self.suffix = "%Y-%m-%d-%H-%M-%S" - ext_match = r"(? str: + def build_file_name(self, prefix: str) -> str: """Override rotation_filename.""" + part_extractor = compile( + r"^" + + escape(f"{prefix}-{self.file_name}-") + + r"([0-9]+)" + + escape(self.file_ext) + + r"$" + ) + + part = ( + max( + chain( + ( + int(match.group(1)) + for path in self.file_folder.iterdir() + if (match := part_extractor.match(path.name)) + ), + DEFAULT_PART, + ) + ) + + 1 + ) + name = f"{self.file_folder}/{prefix}-{self.file_name}-{part}{self.file_ext}" if not callable(self.namer): @@ -107,6 +136,22 @@ def build_file_name(self, prefix: str, part: int) -> str: else: return self.namer(name) + def remove_old_backup(self) -> None: + """Remove old backup files.""" + if self.backupCount > 0: + backups = sorted( + ( + path + for path in self.file_folder.iterdir() + if self.extMatch.match(path.name) + ), + key=getmtime, + ) + + if self.backupCount < (backup_count := len(backups)): + for backup in backups[0 : backup_count - self.backupCount]: + remove(backup) + def doRollover(self) -> None: # noqa: N802 """Override doRollover.""" current_time = int(ttime()) @@ -128,26 +173,9 @@ def doRollover(self) -> None: # noqa: N802 if self.stream: self.stream.close() self.stream = None # type: ignore[assignment] - if self.backupCount > 0: - for i in range(self.backupCount - 1, 0, -1): - sfn = self.build_file_name(prefix, i) - dfn = self.build_file_name(prefix, i + 1) - if exists(sfn): - if exists(dfn): - remove(dfn) - rename(sfn, dfn) - dfn = self.build_file_name(prefix, 1) - if exists(dfn): - remove(dfn) - self.rotate(self.baseFilename, dfn) - else: - part = 1 - while True: - dfn = self.build_file_name(prefix, part) - if not exists(dfn): - self.rotate(self.baseFilename, dfn) - break - part += 1 + + self.rotate(self.baseFilename, self.build_file_name(prefix)) + self.remove_old_backup() if not self.delay: self.stream = self._open() @@ -187,19 +215,14 @@ class Level(IntEnum): logger = getLogger(getenv("LOGGER_NAME", "app")) logger.setLevel(Level[getenv("LOGGER_LEVEL", "DEBUG")].value) -if getenv("ELASTIC_LOGGER") == "true": - handler = FileHandler(getenv("LOGGER_FILE_PATH", "elastic-log.json")) - handler.setFormatter(StdlibFormatter()) - logger.addHandler(handler) -else: - handler = MixedTimedRotatingFileHandler( - getenv("LOGGER_FILE_PATH", "jac-cloud.log"), - when="d", - backup_count=int(getenv("LOGGER_DAILY_MAX_FILE", "-1")), - max_bytes=int(getenv("LOGGER_MAX_FILE_SIZE", "10000000")), - ) - handler.setFormatter(StdlibFormatter()) - logger.addHandler(handler) +handler = MixedTimedRotatingFileHandler( + getenv("LOGGER_FILE_NAMESPACE", "") + getenv("LOGGER_FILE_PATH", "jac-cloud.log"), + when=getenv("LOGGER_TIME_INTERVAL", "d"), + backup_count=int(getenv("LOGGER_MAX_BACKUP", "5")), + max_bytes=int(getenv("LOGGER_MAX_FILE_SIZE", "1000")), +) +handler.setFormatter(StdlibFormatter()) +logger.addHandler(handler) def log_entry(