Configure kanta event loggers at import time, inheriting the root level.

configure_logging() now runs with default arguments when kanta.logging
is imported, attaching a plain stderr handler with propagate=False to
the event loggers (kanta.bootstrap/migration/transaction) that carry
Kanta-rendered output.  No levels are set by default, so event output
inherits the effective root level: a framework switching root between
INFO in development and WARNING in production governs Kanta output too.

Other configure_logging changes: channel enable flags use
logger.disabled (propagate toggling no longer silences now that event
loggers have their own handler), skiproot=False removes Kanta's
handler and re-enables propagation so the root logger renders event
output, and debug=True lifts only the DEBUG-emitting loggers
(bootstrap, migration) to DEBUG instead of setting a level on the
"kanta" parent.
This commit is contained in:
2026-09-16 02:16:58 +00:00
parent a41f34d332
commit 44c1ed191e
5 changed files with 125 additions and 70 deletions
+19
View File
@@ -1,3 +1,5 @@
import logging
import pytest
from kanta.serialization import JsonSerializer, MsgPackSerializer
@@ -12,3 +14,20 @@ from kanta.serialization import JsonSerializer, MsgPackSerializer
)
def format_config(request):
return request.param
@pytest.fixture(autouse=True)
def _kanta_event_loggers_propagate():
"""Let kanta's event loggers propagate so caplog captures their records.
Kanta configures them with ``propagate = False`` at import time, which
would hide their records from pytest's root-logger capture handler.
"""
names = ("kanta.bootstrap", "kanta.migration", "kanta.transaction")
loggers = [logging.getLogger(name) for name in names]
previous = [logger.propagate for logger in loggers]
for logger in loggers:
logger.propagate = True
yield
for logger, propagate in zip(loggers, previous):
logger.propagate = propagate
+19 -16
View File
@@ -38,13 +38,23 @@ def _reset_kanta_loggers():
logger.handlers.clear()
def _setup_logging(**kwargs):
"""Default kanta logging with the event loggers lifted to INFO.
Event loggers inherit the root level (WARNING under pytest); output
assertions need INFO.
"""
configure_logging(**kwargs)
for name in ("kanta.bootstrap", "kanta.migration", "kanta.transaction"):
logging.getLogger(name).setLevel(logging.INFO)
def _change_event(**kwargs) -> LogEvent:
return LogEvent(kind="change", logger=transaction_logger, action="update", **kwargs)
def test_emit_event_falsy_return_stops_chain(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
calls = []
def first(ev):
@@ -60,15 +70,13 @@ def test_emit_event_falsy_return_stops_chain(capsys):
def test_emit_event_truthy_return_falls_back_to_default(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
emit_event(_change_event(), [lambda ev: True])
assert "update" in capsys.readouterr().err
def test_emit_event_mutation_reaches_later_handlers_and_default(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
calls = []
def first(ev):
@@ -86,8 +94,7 @@ def test_emit_event_mutation_reaches_later_handlers_and_default(capsys):
def test_emit_event_handler_error_falls_back_to_default(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
def boom(ev):
raise RuntimeError("broken")
@@ -109,8 +116,7 @@ def test_diff_lines_built_lazily(monkeypatch):
def test_default_emit_created_and_migrated(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
emit_event(LogEvent(kind="created", logger=bootstrap_logger, filename="x.kantadb"))
emit_event(
LogEvent(
@@ -129,8 +135,7 @@ def test_default_emit_created_and_migrated(capsys):
def test_default_emit_strips_ansi_without_color_support(capsys, monkeypatch):
"""NO_COLOR output contains no ANSI codes; FORCE_COLOR keeps them."""
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
monkeypatch.setenv("NO_COLOR", "1")
monkeypatch.delenv("FORCE_COLOR", raising=False)
emit_event(_change_event(diff={"counter": 1}))
@@ -138,8 +143,7 @@ def test_default_emit_strips_ansi_without_color_support(capsys, monkeypatch):
assert "\x1b[" not in err
assert "counter" in err
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
monkeypatch.setenv("FORCE_COLOR", "1")
monkeypatch.delenv("NO_COLOR", raising=False)
emit_event(_change_event(diff={"counter": 1}))
@@ -357,8 +361,7 @@ async def test_event_carries_kanta_instance(tmp_path, format_config):
def test_header_is_settable_and_used_by_default_emit(capsys):
logging.getLogger("kanta").handlers.clear()
configure_logging()
_setup_logging()
def restyle(ev):
ev.header = f"CUSTOM {ev.action}"
+31 -29
View File
@@ -39,33 +39,42 @@ def _reset_kanta_loggers():
def test_configure_logging_defaults():
kanta_logger = logging.getLogger("kanta")
configure_logging()
assert kanta_logger.level == logging.INFO
assert not kanta_logger.propagate
assert kanta_logger.handlers
for name in ("kanta.bootstrap", "kanta.migration", "kanta.transaction"):
logger = logging.getLogger(name)
assert logger.level == logging.NOTSET # inherits the root level
assert not logger.propagate
assert logger.handlers
def test_configure_logging_disables_specific_loggers():
configure_logging(bootstrap=False, migration=False, transaction=False)
assert not logging.getLogger("kanta.bootstrap").propagate
assert not logging.getLogger("kanta.migration").propagate
assert not logging.getLogger("kanta.transaction").propagate
assert logging.getLogger("kanta.bootstrap").disabled
assert logging.getLogger("kanta.migration").disabled
assert logging.getLogger("kanta.transaction").disabled
def test_configure_logging_skiproot_false_leaves_kanta_propagation():
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging(bootstrap=False, skiproot=False)
assert kanta_logger.propagate
assert not kanta_logger.handlers
assert not logging.getLogger("kanta.bootstrap").propagate
def test_configure_logging_skiproot_false_routes_via_root():
configure_logging(skiproot=False)
for name in ("kanta.bootstrap", "kanta.migration", "kanta.transaction"):
logger = logging.getLogger(name)
assert logger.propagate
assert not logger.handlers
def _setup_logging(**kwargs):
"""Default kanta logging with the event loggers lifted to INFO.
Event loggers inherit the root level (WARNING under pytest); output
assertions need INFO.
"""
configure_logging(**kwargs)
for name in ("kanta.bootstrap", "kanta.migration", "kanta.transaction"):
logging.getLogger(name).setLevel(logging.INFO)
def test_log_change_no_diff(capsys):
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging()
_setup_logging()
log_change("test", {})
captured = capsys.readouterr()
assert "test" in captured.err
@@ -74,9 +83,7 @@ def test_log_change_no_diff(capsys):
def test_log_change_appends_extra_string(capsys, monkeypatch):
monkeypatch.setenv("FORCE_COLOR", "1")
monkeypatch.delenv("NO_COLOR", raising=False)
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging()
_setup_logging()
log_change("export", {}, extra="mydb.db")
captured = capsys.readouterr()
assert "export" in captured.err
@@ -84,9 +91,7 @@ def test_log_change_appends_extra_string(capsys, monkeypatch):
def test_log_change_log_diff_false(capsys, monkeypatch):
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging()
_setup_logging()
def _boom(*args, **kwargs):
raise AssertionError("format_diff should not be called")
@@ -99,9 +104,7 @@ def test_log_change_log_diff_false(capsys, monkeypatch):
def test_configure_logging_diff_false(capsys):
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging(diff=False)
_setup_logging(diff=False)
log_change("update", {"counter": 5}, previous={})
captured = capsys.readouterr()
assert "update" in captured.err
@@ -109,10 +112,9 @@ def test_configure_logging_diff_false(capsys):
def test_configure_logging_diff_true_reenables(capsys):
kanta_logger = logging.getLogger("kanta")
kanta_logger.handlers.clear()
configure_logging(diff=False)
_setup_logging(diff=False)
configure_logging(diff=True)
logging.getLogger("kanta.transaction").setLevel(logging.INFO)
log_change("update", {"counter": 5}, previous={})
captured = capsys.readouterr()
assert "counter" in captured.err