File size: 2,127 Bytes
7a1d414
 
 
 
 
 
 
 
 
 
 
 
9bc957e
7a1d414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"""Lightweight logger factory for the SDGen application.

This module centralizes logger configuration to ensure consistent formatting,
file rotation, and prevention of duplicate handlers during repeated imports.
"""

from __future__ import annotations

import logging
from logging import Handler, Logger
from logging.handlers import RotatingFileHandler

from sdgen.config import LOGS_ROOT

# Ensure logs directory exists
LOGS_ROOT.mkdir(parents=True, exist_ok=True)

# Cache prevents repeated handler installation for the same logger name
_LOGGER_CACHE: dict[str, Logger] = {}


def _build_handler() -> Handler:
    """Return a rotating file handler with unified log formatting.

    The handler writes to `app.log` under LOGS_ROOT and uses log rotation
    to cap file size and maintain up to 3 backups.
    """
    log_file = LOGS_ROOT / "app.log"
    handler = RotatingFileHandler(
        filename=log_file,
        maxBytes=5_000_000,  # ~5 MB
        backupCount=3,
    )
    fmt = "%(asctime)s [%(name)s] [%(levelname)s] %(message)s"
    handler.setFormatter(logging.Formatter(fmt))
    return handler


def get_logger(name: str) -> Logger:
    """Return a configured logger with rotating file and console handlers.

    The returned logger:
    - uses INFO level by default
    - writes to both stdout and a rotating log file
    - does not propagate to root logger
    - never duplicates handlers for the same name

    Args:
        name: Distinct logger name, generally the module name.

    Returns:
        A configured `logging.Logger` instance.
    """
    if name in _LOGGER_CACHE:
        return _LOGGER_CACHE[name]

    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)

    # Guard against accidentally adding handlers multiple times
    if not logger.handlers:
        logger.addHandler(_build_handler())

        stream = logging.StreamHandler()
        stream.setFormatter(
            logging.Formatter("%(asctime)s [%(name)s]" + "[%(levelname)s] %(message)s")
        )
        logger.addHandler(stream)

    logger.propagate = False
    _LOGGER_CACHE[name] = logger
    return logger