123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 |
- from __future__ import annotations
- import os
- import logging
- from pathlib import Path
- from logging.handlers import RotatingFileHandler
- from typing import Optional
- _CONFIGURED = False
- def _project_root() -> Path:
- # Resolve repo root from this file location: app/core/logger.py -> repo/app/core
- return Path(__file__).resolve().parents[2]
- def configure_logging(level: Optional[str] = None, log_dir: Optional[str] = None) -> None:
- global _CONFIGURED
- if _CONFIGURED:
- return
- # Determine log level
- level_name = (level or os.getenv("LOG_LEVEL") or "INFO").upper()
- log_level = getattr(logging, level_name, logging.INFO)
- # Determine logs directory
- base_dir = Path(log_dir) if log_dir else _project_root() / "logs"
- base_dir.mkdir(parents=True, exist_ok=True)
- # Root logger configuration
- root = logging.getLogger()
- root.setLevel(log_level)
- fmt = logging.Formatter(
- fmt="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
- datefmt="%Y-%m-%d %H:%M:%S",
- )
- file_handler = RotatingFileHandler(base_dir / "app.log", maxBytes=1_000_000, backupCount=3)
- file_handler.setFormatter(fmt)
- file_handler.setLevel(log_level)
- root.addHandler(file_handler)
- # Stream warnings+ to stderr for container visibility
- stream = logging.StreamHandler()
- stream.setLevel(logging.WARNING)
- stream.setFormatter(fmt)
- root.addHandler(stream)
- _CONFIGURED = True
- def get_logger(name: Optional[str] = None) -> logging.Logger:
- if not _CONFIGURED:
- configure_logging()
- return logging.getLogger(name or "app")
|