Files
calvana/ayn-antivirus/ayn_antivirus/utils/logger.py

102 lines
2.7 KiB
Python

"""Logging setup for AYN Antivirus.
Provides a one-call ``setup_logging()`` function that configures a
rotating file handler and an optional console handler with consistent
formatting across the entire application.
"""
from __future__ import annotations
import logging
import os
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Optional
from ayn_antivirus.constants import DEFAULT_LOG_PATH
# ---------------------------------------------------------------------------
# Format
# ---------------------------------------------------------------------------
_LOG_FORMAT = "[%(asctime)s] %(levelname)s %(name)s: %(message)s"
_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
# Rotating handler defaults.
_MAX_BYTES = 10 * 1024 * 1024 # 10 MB
_BACKUP_COUNT = 5
def setup_logging(
log_dir: str | Path = DEFAULT_LOG_PATH,
level: int | str = logging.INFO,
console: bool = True,
filename: str = "ayn-antivirus.log",
) -> logging.Logger:
"""Configure the root ``ayn_antivirus`` logger.
Parameters
----------
log_dir:
Directory for the rotating log file. Created automatically.
level:
Logging level (``logging.DEBUG``, ``"INFO"``, etc.).
console:
If ``True``, also emit to stderr.
filename:
Name of the log file inside *log_dir*.
Returns
-------
logging.Logger
The configured ``ayn_antivirus`` logger.
"""
if isinstance(level, str):
level = getattr(logging, level.upper(), logging.INFO)
root = logging.getLogger("ayn_antivirus")
root.setLevel(level)
# Avoid duplicate handlers on repeated calls.
if root.handlers:
return root
formatter = logging.Formatter(_LOG_FORMAT, datefmt=_DATE_FORMAT)
# --- Rotating file handler ---
log_path = Path(log_dir)
try:
log_path.mkdir(parents=True, exist_ok=True)
fh = RotatingFileHandler(
str(log_path / filename),
maxBytes=_MAX_BYTES,
backupCount=_BACKUP_COUNT,
encoding="utf-8",
)
fh.setLevel(level)
fh.setFormatter(formatter)
root.addHandler(fh)
except OSError:
# If we can't write to the log dir, fall back to console only.
pass
# --- Console handler ---
if console:
ch = logging.StreamHandler(sys.stderr)
ch.setLevel(level)
ch.setFormatter(formatter)
root.addHandler(ch)
return root
def get_logger(name: str) -> logging.Logger:
"""Return a child logger under the ``ayn_antivirus`` namespace.
Example::
logger = get_logger("scanners.file")
# → logging.getLogger("ayn_antivirus.scanners.file")
"""
return logging.getLogger(f"ayn_antivirus.{name}")