from __future__ import annotations import logging import os from pathlib import Path from typing import Optional # Lightweight logging helper for the Gradio demo. # The MCP server has its own logging configuration; this module keeps # demo-specific logs in a separate file referenced from the UI. LOGGER_NAME = "aileen3_demo" LOG_LEVEL_ENV = "AILEEN3_DEMO_LOGLEVEL" _configured = False _log_file_path: Path | None = None def _resolve_level() -> int: level_name = (os.environ.get(LOG_LEVEL_ENV) or "INFO").upper() level = getattr(logging, level_name, None) if isinstance(level, int): return level try: return int(level_name) except Exception: return logging.INFO def _pick_log_path() -> Path: primary_base = Path(os.environ.get("AILEEN3_CACHE_DIR", Path.home() / ".cache" / "aileen3")) primary = primary_base / "logs" / "aileen3-demo.log" fallback = Path.cwd() / ".aileen3_logs" / "aileen3-demo.log" for candidate in (primary, fallback): try: candidate.parent.mkdir(parents=True, exist_ok=True) with open(candidate, "a", encoding="utf-8"): pass return candidate except PermissionError: continue except OSError: continue # Last resort: stderr-only logger return fallback def _ensure_logger() -> None: global _configured, _log_file_path if _configured: return level = _resolve_level() logger = logging.getLogger(LOGGER_NAME) logger.setLevel(level) logger.propagate = False log_path = _pick_log_path() _log_file_path = log_path try: handler = logging.FileHandler(log_path, encoding="utf-8") handler.setLevel(level) handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s")) logger.addHandler(handler) except Exception: # Fallback to stderr only handler = logging.StreamHandler() handler.setLevel(level) handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s")) logger.addHandler(handler) _configured = True def get_demo_logger(name: Optional[str] = None) -> logging.Logger: """Return a logger that writes into a cache or repo-local log file.""" _ensure_logger() return logging.getLogger(name or LOGGER_NAME) def get_demo_log_path() -> Path: _ensure_logger() return _log_file_path or Path(".aileen3_logs/aileen3-demo.log") __all__ = ["get_demo_logger", "get_demo_log_path"]