import logging from logging.handlers import RotatingFileHandler from pathlib import Path def configure_logging(log_file: Path) -> None: """Configure console and file logging once for local service runs.""" log_file.parent.mkdir(parents=True, exist_ok=True) root_logger = logging.getLogger() root_logger.setLevel(logging.INFO) if not _has_handler("signalscout-console"): # Console logs are for the terminal window that runs `make run`. console_handler = logging.StreamHandler() console_handler.name = "signalscout-console" console_handler.setFormatter(_formatter()) root_logger.addHandler(console_handler) if not _has_handler("signalscout-file"): # File logs persist agent failures after the terminal scrollback is gone. file_handler = RotatingFileHandler( log_file, maxBytes=2_000_000, backupCount=3, encoding="utf-8", ) file_handler.name = "signalscout-file" file_handler.setFormatter(_formatter()) root_logger.addHandler(file_handler) def _formatter() -> logging.Formatter: # Include module name so source, API, and runner failures are easy to separate. return logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s") def _has_handler(name: str) -> bool: # Uvicorn imports the app during startup, so handlers must be idempotent. return any(handler.name == name for handler in logging.getLogger().handlers)