from __future__ import annotations import logging import os from pathlib import Path # Logging paths used by the MCP package. # The demo UI links directly to these files, so keep the layout stable. BASE_CACHE = Path(os.environ.get("AILEEN3_CACHE_DIR", Path.home() / ".cache" / "aileen3")) LOG_DIR = BASE_CACHE / "logs" LOG_FILE = LOG_DIR / "aileen3-mcp.log" PACKAGE_LOGGER_NAME = "aileen3_mcp" def _resolve_level() -> int: """Resolve the log level from ``AILEEN3_LOGLEVEL`` or fall back to INFO.""" level_name = os.environ.get("AILEEN3_LOGLEVEL", "").upper() or "INFO" level = getattr(logging, level_name, None) if isinstance(level, int): return level # Fallback for non-standard levels try: return int(level_name) except Exception: return logging.INFO def configure_logging() -> Path: """Configure package logging without turning on DEBUG for third-party libs.""" LOG_DIR.mkdir(parents=True, exist_ok=True) level = _resolve_level() pkg_logger = logging.getLogger(PACKAGE_LOGGER_NAME) pkg_logger.setLevel(level) pkg_logger.propagate = False # keep third-party noise out of our handler existing = None for h in pkg_logger.handlers: if isinstance(h, logging.FileHandler) and Path(getattr(h, "baseFilename", "")) == LOG_FILE: existing = h break if existing: existing.setLevel(level) else: handler = logging.FileHandler(LOG_FILE, encoding="utf-8") handler.setLevel(level) handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s")) pkg_logger.addHandler(handler) return LOG_FILE