Clarify diagnostic log messages; simplify describe_callback fallback
Unnamed callables (partials, callable instances) are described by type name only: docstrings are shown only for named callables, avoiding misleading class docstrings in failure messages. Diagnostic messages revised for clarity when mixed with application logs; logger.exception() messages no longer repeat the exception text, which the traceback already shows.
This commit is contained in:
@@ -8,7 +8,6 @@ import contextlib
|
||||
import importlib
|
||||
import importlib.metadata
|
||||
import importlib.util
|
||||
import logging
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
+19
-12
@@ -61,9 +61,13 @@ def describe_callback(callback: Callable[..., Any]) -> str:
|
||||
"""Return ``name (docstring first line)`` identifying *callback*.
|
||||
|
||||
Used in failure messages so a bare log line names the function that
|
||||
failed, e.g. ``myformatter (Concise log formatter)``.
|
||||
failed, e.g. ``myformatter (Concise log formatter)``. Callables without
|
||||
a ``__name__`` (partials, callable instances, ...) are described by
|
||||
their type name only: less information, but never wrong information.
|
||||
"""
|
||||
name = getattr(callback, "__name__", None) or type(callback).__name__
|
||||
name = getattr(callback, "__name__", None)
|
||||
if not isinstance(name, str):
|
||||
return type(callback).__name__
|
||||
doc = inspect.getdoc(callback)
|
||||
if doc:
|
||||
return f"{name} ({doc.splitlines()[0]})"
|
||||
@@ -75,14 +79,13 @@ def callback_error_reporter(
|
||||
) -> Callable[[Exception, Callable[..., Any]], None]:
|
||||
"""Return an ``on_error`` reporter for :meth:`CallbackRegistry.invoke`.
|
||||
|
||||
The returned callable logs ``Kanta.<kind> <name (docstring)> failed:
|
||||
<error>`` for each failing callback; invoke continues with the rest.
|
||||
The returned callable logs ``Kanta.<kind> <name (docstring)> failed``
|
||||
with the traceback for each failing callback; invoke continues with
|
||||
the rest.
|
||||
"""
|
||||
|
||||
def _report(callback_error: Exception, callback: Callable[..., Any]) -> None:
|
||||
_logger.exception(
|
||||
"Kanta.%s %s failed: %s", kind, describe_callback(callback), callback_error
|
||||
)
|
||||
_logger.exception("Kanta.%s %s failed", kind, describe_callback(callback))
|
||||
|
||||
return _report
|
||||
|
||||
@@ -275,12 +278,14 @@ class CallbackRegistry:
|
||||
|
||||
def build_logfmt(self, ctx: InjectionContext) -> Callable[[Any, str], str | None]:
|
||||
"""Build a chained formatter from registered logfmt callbacks."""
|
||||
formatters: list[tuple[Callable[[Any, str], str | None], str | None]] = []
|
||||
formatters: list[
|
||||
tuple[Callable[[Any, str], str | None], str | None, Callable[..., Any]]
|
||||
] = []
|
||||
for spec in self._logfmt_callbacks:
|
||||
if isinstance(spec, _LogFmtClassSpec):
|
||||
kwargs = self._build_kwargs(spec.inject_params, ctx)
|
||||
instance: Callable[[Any, str], str | None] = spec.cls(**kwargs)
|
||||
formatters.append((instance, spec.path))
|
||||
formatters.append((instance, spec.path, spec.cls))
|
||||
else:
|
||||
kwargs = self._build_kwargs(spec.inject_params, ctx)
|
||||
|
||||
@@ -300,10 +305,10 @@ class CallbackRegistry:
|
||||
|
||||
return formatter
|
||||
|
||||
formatters.append((make_formatter(), spec.path))
|
||||
formatters.append((make_formatter(), spec.path, spec.callback))
|
||||
|
||||
def format_value(value: Any, path: str) -> str | None:
|
||||
for fn, pattern in formatters:
|
||||
for fn, pattern, callback in formatters:
|
||||
if pattern is not None and path != pattern:
|
||||
continue
|
||||
try:
|
||||
@@ -311,7 +316,9 @@ class CallbackRegistry:
|
||||
except Exception:
|
||||
# Formatting must never break functionality; a failing
|
||||
# callback is reported and treated as a fall-through.
|
||||
_logger.exception("Kanta.logfmt %s failed", describe_callback(fn))
|
||||
_logger.exception(
|
||||
"Kanta.logfmt %s failed", describe_callback(callback)
|
||||
)
|
||||
continue
|
||||
if resolved is not None:
|
||||
return resolved
|
||||
|
||||
+7
-4
@@ -17,6 +17,7 @@ from typing import Any
|
||||
|
||||
import msgspec
|
||||
|
||||
from kanta.callbacks import describe_callback
|
||||
from kanta.serialization.base import _apply, unmarshal
|
||||
from kanta.tty import Line, displaywidth, strip_ansi, use_color
|
||||
|
||||
@@ -158,13 +159,16 @@ def emit_event(
|
||||
try:
|
||||
proceed = handler(ev)
|
||||
except Exception:
|
||||
_logger.exception("Kanta.logemit callback failed, using default formatting")
|
||||
_logger.exception(
|
||||
"Kanta.logemit %s failed, using default formatting",
|
||||
describe_callback(handler),
|
||||
)
|
||||
break
|
||||
if not proceed:
|
||||
return
|
||||
render(ev)
|
||||
except Exception:
|
||||
_logger.exception("failed to emit %s log event", ev.kind)
|
||||
_logger.exception("Kanta failed to emit %s log event", ev.kind)
|
||||
|
||||
|
||||
def _maybe_strip(text: str) -> str:
|
||||
@@ -604,8 +608,7 @@ def configure_logging(
|
||||
logger and follow the application's root logging configuration.
|
||||
|
||||
No levels are set by default: the event loggers inherit the effective
|
||||
level of the root logger, so a framework switching root between INFO in
|
||||
development and WARNING in production governs Kanta output too.
|
||||
level of the root logger.
|
||||
|
||||
Args:
|
||||
skiproot: If ``True`` (default), event loggers print through Kanta's
|
||||
|
||||
@@ -115,7 +115,9 @@ class PersistenceMixin:
|
||||
InjectionContext(error=e, kanta=self._kanta),
|
||||
on_error=callback_error_reporter("fatal_error"),
|
||||
)
|
||||
_logger.error("Background flush loop stopped: %s", e)
|
||||
_logger.error(
|
||||
"Kanta background flush failed; automatic flushing stopped: %s", e
|
||||
)
|
||||
break
|
||||
|
||||
def maybe_snapshot(self) -> None:
|
||||
|
||||
+2
-1
@@ -216,7 +216,8 @@ def execute_rotation(
|
||||
f.truncate(plan.cutoff_end)
|
||||
if log:
|
||||
_logger.info(
|
||||
"rotated %s: kept %d change record(s), history before %s moved to %s",
|
||||
"Rotated database %s: kept %d change record(s), "
|
||||
"moved history before %s to %s",
|
||||
path,
|
||||
plan.retained_changes,
|
||||
plan.rotated_ts.isoformat(),
|
||||
|
||||
Reference in New Issue
Block a user