Treat dict log_config without a version key as an overlay on uvicorn defaults.
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).
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""Tests for the fastapi_vue package."""
|
||||
@@ -0,0 +1,60 @@
|
||||
"""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"
|
||||
Reference in New Issue
Block a user