🎨 Improved logging
- One unlimited log file per session - Up to 3 session logs kept at any time - Log compressed via lzma
This commit is contained in:
70
src/logging/session_file_handler.py
Normal file
70
src/logging/session_file_handler.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import lzma
|
||||||
|
from io import StringIO
|
||||||
|
from logging import StreamHandler
|
||||||
|
from lzma import FORMAT_XZ, PRESET_DEFAULT
|
||||||
|
from os import PathLike
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class SessionFileHandler(StreamHandler):
|
||||||
|
"""
|
||||||
|
A logging handler that writes to a new file on every app restart.
|
||||||
|
The files are compressed and older sessions logs are kept up to a small limit.
|
||||||
|
"""
|
||||||
|
|
||||||
|
backup_count: int
|
||||||
|
filename: Path
|
||||||
|
log_file: StringIO = None
|
||||||
|
|
||||||
|
def create_dir(self) -> None:
|
||||||
|
"""Create the log dir if needed"""
|
||||||
|
self.filename.parent.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
def rotate_file(self, file: Path):
|
||||||
|
"""Rotate a file's number suffix and remove it if it's too old"""
|
||||||
|
|
||||||
|
# Skip non interesting dir entries
|
||||||
|
if not (file.is_file() and file.name.startswith(self.filename.name)):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Compute the new number suffix
|
||||||
|
suffixes = file.suffixes
|
||||||
|
has_number = len(suffixes) != len(self.filename.suffixes)
|
||||||
|
current_number = 0 if not has_number else int(suffixes[-1][1:])
|
||||||
|
new_number = current_number + 1
|
||||||
|
|
||||||
|
# Rename with new number suffix
|
||||||
|
if has_number:
|
||||||
|
suffixes.pop()
|
||||||
|
suffixes.append(f".{new_number}")
|
||||||
|
stem = file.name.split(".", maxsplit=1)[0]
|
||||||
|
new_name = stem + "".join(suffixes)
|
||||||
|
print(f"Log file renamed: {file.name} -> {new_name}")
|
||||||
|
file = file.rename(file.with_name(new_name))
|
||||||
|
|
||||||
|
# Remove older files
|
||||||
|
if new_number > self.backup_count:
|
||||||
|
print(f"Log file deleted: {file.name}")
|
||||||
|
file.unlink()
|
||||||
|
return
|
||||||
|
|
||||||
|
def rotate(self) -> None:
|
||||||
|
"""Rotate the numbered suffix on the log files and remove old ones"""
|
||||||
|
files = list(self.filename.parent.iterdir())
|
||||||
|
files.sort(key=lambda file: file.name, reverse=True)
|
||||||
|
for file in files:
|
||||||
|
self.rotate_file(file)
|
||||||
|
|
||||||
|
def __init__(self, filename: PathLike, backup_count: int = 2) -> None:
|
||||||
|
self.filename = Path(filename)
|
||||||
|
self.backup_count = backup_count
|
||||||
|
self.create_dir()
|
||||||
|
self.rotate()
|
||||||
|
self.log_file = lzma.open(
|
||||||
|
self.filename, "at", format=FORMAT_XZ, preset=PRESET_DEFAULT
|
||||||
|
)
|
||||||
|
super().__init__(self.log_file)
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.log_file.close()
|
||||||
|
super().close()
|
||||||
@@ -8,18 +8,14 @@ from src import shared
|
|||||||
def setup_logging():
|
def setup_logging():
|
||||||
"""Intitate the app's logging"""
|
"""Intitate the app's logging"""
|
||||||
|
|
||||||
# Prepare the log file
|
is_dev = shared.PROFILE == "development"
|
||||||
log_dir = shared.data_dir / "cartridges" / "logs"
|
profile_app_log_level = "DEBUG" if is_dev else "INFO"
|
||||||
log_dir.mkdir(exist_ok=True)
|
profile_lib_log_level = "INFO" if is_dev else "WARNING"
|
||||||
log_file_path = log_dir / "cartridges.log"
|
|
||||||
log_file_max_size_bytes = 8 * 10**6 # 8 MB
|
|
||||||
|
|
||||||
# Define log levels
|
|
||||||
profile_app_log_level = "DEBUG" if shared.PROFILE == "development" else "INFO"
|
|
||||||
profile_lib_log_level = "INFO" if shared.PROFILE == "development" else "WARNING"
|
|
||||||
app_log_level = os.environ.get("LOGLEVEL", profile_app_log_level).upper()
|
app_log_level = os.environ.get("LOGLEVEL", profile_app_log_level).upper()
|
||||||
lib_log_level = os.environ.get("LIBLOGLEVEL", profile_lib_log_level).upper()
|
lib_log_level = os.environ.get("LIBLOGLEVEL", profile_lib_log_level).upper()
|
||||||
|
|
||||||
|
log_filename = shared.data_dir / "cartridges" / "logs" / "cartridges.log.xz"
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"formatters": {
|
"formatters": {
|
||||||
@@ -33,12 +29,11 @@ def setup_logging():
|
|||||||
},
|
},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"file_handler": {
|
"file_handler": {
|
||||||
"class": "logging.handlers.RotatingFileHandler",
|
"class": "src.logging.session_file_handler.SessionFileHandler",
|
||||||
"formatter": "file_formatter",
|
"formatter": "file_formatter",
|
||||||
"level": "DEBUG",
|
"level": "DEBUG",
|
||||||
"filename": log_file_path,
|
"filename": log_filename,
|
||||||
"maxBytes": log_file_max_size_bytes,
|
"backup_count": 2,
|
||||||
"backupCount": 1,
|
|
||||||
},
|
},
|
||||||
"app_console_handler": {
|
"app_console_handler": {
|
||||||
"class": "logging.StreamHandler",
|
"class": "logging.StreamHandler",
|
||||||
|
|||||||
Reference in New Issue
Block a user