diff --git a/pallas/log.py b/pallas/log.py index d7df4cf..f3589e8 100644 --- a/pallas/log.py +++ b/pallas/log.py @@ -20,6 +20,7 @@ import logging import os import re from datetime import datetime, timezone +from pathlib import Path class _JSONFormatter(logging.Formatter): @@ -68,21 +69,55 @@ class _HealthAccessFilter(logging.Filter): return True +def _level_from_fastagent_config() -> str | None: + """Return the ``logger.level`` string from ``fastagent.config.yaml`` if set. + + We intentionally parse the YAML by hand (rather than importing fast-agent's + config loader) because ``setup_logging`` runs *before* fast-agent is + initialised — importing its config machinery here would reverse the + bootstrap order and swallow our first few log lines. + """ + cfg_path = Path.cwd() / "fastagent.config.yaml" + if not cfg_path.exists(): + return None + try: + import yaml + + with open(cfg_path) as fh: + data = yaml.safe_load(fh) or {} + except Exception: + return None + logger_cfg = data.get("logger") or {} + level = logger_cfg.get("level") + return str(level) if level else None + + def setup_logging() -> None: """Configure Pallas logging. - - ``pallas.*`` logger: INFO by default, JSON to stdout. Override via the - ``PALLAS_LOG_LEVEL`` environment variable (``DEBUG``, ``INFO``, - ``WARNING``, ``ERROR``). DEBUG unlocks bearer-forwarding diagnostics - on the ``pallas.forward`` and ``pallas.auth`` loggers — essential when - troubleshooting Mnemosyne / workspace-scoped agent calls. + Log level resolution order (first match wins): + + 1. ``PALLAS_LOG_LEVEL`` environment variable — explicit override. + 2. ``logger.level`` in ``fastagent.config.yaml`` — unified control knob + so bumping fast-agent's level also flips on Pallas's bearer-forwarding + diagnostics. + 3. ``INFO``. + + ``DEBUG`` unlocks diagnostics on the ``pallas.forward`` and + ``pallas.auth`` loggers — essential when troubleshooting Mnemosyne / + workspace-scoped agent calls. + - ``httpx`` / ``httpcore``: WARNING (prevent request-level debug flooding) - ``uvicorn.access``: health path filter applied """ handler = logging.StreamHandler() handler.setFormatter(_JSONFormatter()) - level_name = os.environ.get("PALLAS_LOG_LEVEL", "INFO").upper() + level_name = ( + os.environ.get("PALLAS_LOG_LEVEL") + or _level_from_fastagent_config() + or "INFO" + ).upper() level = getattr(logging, level_name, logging.INFO) pallas_logger = logging.getLogger("pallas")