A hack to silence INFO level log chatter from uvicorn, while still doing this via log config.

This commit is contained in:
2026-08-31 16:22:16 +00:00
parent 5dbe9a0dcd
commit cb0b1f067d
+25 -10
View File
@@ -104,6 +104,18 @@ class WebSocketChatterFilter(logging.Filter):
return not msg.startswith(self._PREFIXES) return not msg.startswith(self._PREFIXES)
class UvicornQuietFilter(logging.Filter):
"""Silence uvicorn's routine chatter (startup/shutdown lines, etc.).
Handler-side, not a logger level: uvicorn's ``configure_logging``
re-applies ``log_level`` to its loggers after ``dictConfig``, which would
override a level lifted in the config dict.
"""
def filter(self, record: logging.LogRecord) -> bool:
return not (record.name.startswith("uvicorn") and record.levelno < logging.WARNING)
_installed = False _installed = False
@@ -137,14 +149,15 @@ def patch_log_config(log_config, *, access_log: bool = True): # noqa: ANN001, A
untouched. untouched.
Always adds an unreferenced NullHandler whose Formatter instantiation Always adds an unreferenced NullHandler whose Formatter instantiation
loads tracerite in every process uvicorn applies the config in, a loads tracerite in every process uvicorn applies the config in, filters
filter dropping stock uvicorn's WebSocket chatter from ``uvicorn.error``, on the default handler dropping stock uvicorn's WebSocket chatter and
an emoji-level-prefix Formatter in place of uvicorn's stock ``default`` routine INFO lines, an emoji-level-prefix Formatter in place of
formatter (a user-supplied one wins), a root logger entry so uvicorn's stock ``default`` formatter (a user-supplied one wins), a root
``logging.info()`` et al. print through the default handler, and a logger entry so ``logging.info()`` et al. print through the default
no-prefix ``kanta`` logger entry (likewise). With ``access_log``, handler, and a no-prefix ``kanta`` logger entry (likewise).
additionally rewires the ``access`` formatter to our Formatter and With ``access_log``, additionally rewires the ``access`` formatter to
attaches its handler to our ``fastapi_vue.access`` logger. We must not our Formatter and attaches its handler to our ``fastapi_vue.access``
logger. We must not
attach handlers to ``uvicorn.access``: uvicorn gates its own attach handlers to ``uvicorn.access``: uvicorn gates its own
protocol-level access logging on ``uvicorn.access.hasHandlers()``. protocol-level access logging on ``uvicorn.access.hasHandlers()``.
""" """
@@ -162,9 +175,11 @@ def patch_log_config(log_config, *, access_log: bool = True): # noqa: ANN001, A
with suppress(Exception): with suppress(Exception):
filters = config.setdefault("filters", {}) filters = config.setdefault("filters", {})
filters["ws_chatter"] = {"()": "fastapi_vue.logging.WebSocketChatterFilter"} filters["ws_chatter"] = {"()": "fastapi_vue.logging.WebSocketChatterFilter"}
filters["uvicorn_quiet"] = {"()": "fastapi_vue.logging.UvicornQuietFilter"}
handler_filters = config["handlers"]["default"].setdefault("filters", []) handler_filters = config["handlers"]["default"].setdefault("filters", [])
if "ws_chatter" not in handler_filters: for name in ("ws_chatter", "uvicorn_quiet"):
handler_filters.append("ws_chatter") if name not in handler_filters:
handler_filters.append(name)
# Emoji level prefixes for ordinary logs, replacing uvicorn's stock # Emoji level prefixes for ordinary logs, replacing uvicorn's stock
# default formatter; a user-supplied default formatter is left alone. # default formatter; a user-supplied default formatter is left alone.