A partial log_config (no "version" key) is deep-merged over uvicorn's
default config before our patching, so users pass only their
customizations, e.g. log_config={"loggers": {"myapp": {"level":
"DEBUG"}}}. Previously such dicts crashed at startup: dictConfig
requires a version, and our root entry referenced a "default" handler
that might not exist; that reference is now only added when the handler
exists. The stock default formatter is replaced only when untouched,
so an overlaid fmt survives. Dicts with version and non-dict configs
behave as before. Documented in the README logging section, with tests
under fastapi-vue/tests (each patched config validated through
dictConfig).
61 lines
2.5 KiB
Python
61 lines
2.5 KiB
Python
"""Tests for fastapi_vue.logging.patch_log_config overlay behavior."""
|
|
|
|
import logging.config
|
|
|
|
from fastapi_vue.logging import patch_log_config
|
|
|
|
|
|
def test_empty_dict_overlays_uvicorn_defaults() -> None:
|
|
"""An empty dict is a partial config: merged over uvicorn's defaults."""
|
|
config = patch_log_config({})
|
|
assert config["version"] == 1
|
|
assert config["disable_existing_loggers"] is False
|
|
assert config["root"]["handlers"] == ["default"]
|
|
assert "uvicorn" in config["loggers"]
|
|
logging.config.dictConfig(config) # must be a valid, complete config
|
|
|
|
|
|
def test_partial_logger_customization() -> None:
|
|
"""The documented use case: only the customization, no boilerplate."""
|
|
config = patch_log_config({"loggers": {"kanta": {"level": "DEBUG"}}})
|
|
assert config["loggers"]["kanta"] == {"level": "DEBUG"}
|
|
assert config["loggers"]["uvicorn"]["handlers"] == ["default"]
|
|
assert config["loggers"]["watchfiles.main"]["level"] == "WARNING"
|
|
logging.config.dictConfig(config)
|
|
assert logging.getLogger("kanta").level == logging.DEBUG
|
|
|
|
|
|
def test_overlay_root_level_wins() -> None:
|
|
"""User-supplied root level is kept; our handler wiring still applies."""
|
|
config = patch_log_config({"root": {"level": "ERROR"}})
|
|
assert config["root"]["level"] == "ERROR"
|
|
assert config["root"]["handlers"] == ["default"]
|
|
logging.config.dictConfig(config)
|
|
assert logging.getLogger().level == logging.ERROR
|
|
|
|
|
|
def test_overlay_formatter_customization_keeps_stock_siblings() -> None:
|
|
"""A user formatter replaces ours; the access formatter still works."""
|
|
config = patch_log_config({"formatters": {"default": {"fmt": "%(name)s %(message)s"}}})
|
|
assert config["formatters"]["default"] == {
|
|
"()": "uvicorn.logging.DefaultFormatter", # stock class, user's fmt
|
|
"fmt": "%(name)s %(message)s",
|
|
"use_colors": None,
|
|
}
|
|
assert "access" in config["formatters"]
|
|
logging.config.dictConfig(config)
|
|
|
|
|
|
def test_full_config_used_as_is() -> None:
|
|
"""A dict with version is complete: no uvicorn loggers appear."""
|
|
config = patch_log_config({"version": 1})
|
|
assert "uvicorn" not in config.get("loggers", {})
|
|
# No "default" handler exists, so root must not reference one.
|
|
assert "handlers" not in config["root"]
|
|
logging.config.dictConfig(config)
|
|
|
|
|
|
def test_non_dict_passes_through() -> None:
|
|
"""Non-dict configs (e.g. an ini file path) are returned untouched."""
|
|
assert patch_log_config("logging.ini") == "logging.ini"
|