feat(log): suppress successful MCP access logs in health filter

Extend `_HealthAccessFilter` to also drop uvicorn access log lines for
successful `POST /mcp` requests, in addition to the existing
`/live`, `/ready`, and `/metrics` health probes.

**Why:** Every Daedalus health poll and tool call hits the single `/mcp`
route. Pallas already emits structured `mcp_request_start` /
`mcp_request_complete` logs at the agent layer, making the uvicorn
access line pure duplication and noise in syslog.

**How:**
- Replace the simple substring list `_HEALTH_PATHS` with compiled regex
  patterns (`_HEALTH_PATH_RE`, `_MCP_RE`) for more precise path matching
- Add `_SUCCESS_STATUS_RE` to only suppress 1xx/2xx/3xx responses;
  non-successful responses (4xx, 5xx) still pass through as real signals
- Update docstring to document the new suppression rules clearly
This commit is contained in:
2026-04-18 07:59:23 -04:00
parent c18a477cda
commit a5b4650dff

View File

@@ -17,6 +17,7 @@ Level conventions (Ouranos Lab — Python services use UPPERCASE):
import json import json
import logging import logging
import re
from datetime import datetime, timezone from datetime import datetime, timezone
@@ -37,17 +38,33 @@ class _JSONFormatter(logging.Formatter):
class _HealthAccessFilter(logging.Filter): 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. Health check success is the *absence* of errors, not the presence of 200s
Logging every HAProxy probe at INFO floods syslog with noise. (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: def filter(self, record: logging.LogRecord) -> bool:
msg = record.getMessage() 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: def setup_logging() -> None: