diff --git a/pallas/log.py b/pallas/log.py index fe4beaf..15b94db 100644 --- a/pallas/log.py +++ b/pallas/log.py @@ -17,6 +17,7 @@ Level conventions (Ouranos Lab — Python services use UPPERCASE): import json import logging +import re from datetime import datetime, timezone @@ -37,17 +38,33 @@ class _JSONFormatter(logging.Formatter): class _HealthAccessFilter(logging.Filter): - """Drop uvicorn access log lines for health/metrics endpoints. + """Drop uvicorn access log lines for routine 2xx/3xx probes. - Health check success is the *absence* of errors, not the presence of 200s. - Logging every HAProxy probe at INFO floods syslog with noise. + Health check success is the *absence* of errors, not the presence of 200s + (Ouranos Red Panda standard). Suppresses: + + * Successful probes to ``/live``, ``/ready``, ``/metrics`` + * Successful ``POST /mcp`` calls — the MCP endpoint is single-route, every + Daedalus health poll and tool call lands there. Pallas already emits + its own structured ``mcp_request_start`` / ``mcp_request_complete`` logs + at the agent layer, so the uvicorn access line is pure duplication. + + Non-2xx/3xx responses still pass through — a failing probe or a 5xx on + ``/mcp`` is a real signal. """ - _HEALTH_PATHS = (" GET /live ", " GET /ready ", " GET /metrics ") + _HEALTH_PATH_RE = re.compile(r'"GET /(?:live|ready|metrics)(?:/|\?|\s|")') + _MCP_RE = re.compile(r'"POST /mcp(?:/|\?|\s|")') + _SUCCESS_STATUS_RE = re.compile(r'" (?:1\d\d|2\d\d|3\d\d)(?:\s|$)') def filter(self, record: logging.LogRecord) -> bool: msg = record.getMessage() - return not any(path in msg for path in self._HEALTH_PATHS) + is_routine = bool( + self._HEALTH_PATH_RE.search(msg) or self._MCP_RE.search(msg) + ) + if is_routine and self._SUCCESS_STATUS_RE.search(msg): + return False + return True def setup_logging() -> None: