Route all diagnostic logging through the plain "kanta" logger.
Module loggers used __name__, splitting diagnostics across eight module-named loggers and colliding kanta.transaction with the transaction event channel. Diagnostics (integrity errors, flush failures, rotation notes) are few; they now all go through the "kanta" logger, following the application's root logging configuration like any ordinary library output.
This commit is contained in:
+2
-6
@@ -17,7 +17,7 @@ from typing import Any
|
|||||||
import msgspec
|
import msgspec
|
||||||
|
|
||||||
from kanta import Kanta
|
from kanta import Kanta
|
||||||
from kanta.callbacks import InjectionContext
|
from kanta.callbacks import InjectionContext, callback_error_reporter
|
||||||
from kanta.exceptions import DatabaseError, DataIntegrityError, ReplayError
|
from kanta.exceptions import DatabaseError, DataIntegrityError, ReplayError
|
||||||
from kanta.grep import GrepPattern, evaluate
|
from kanta.grep import GrepPattern, evaluate
|
||||||
from kanta.logging import (
|
from kanta.logging import (
|
||||||
@@ -50,8 +50,6 @@ EXIT_PARSE_ERROR = 10
|
|||||||
EXIT_MIGRATION_ERROR = 20
|
EXIT_MIGRATION_ERROR = 20
|
||||||
EXIT_VALIDATION_ERROR = 21
|
EXIT_VALIDATION_ERROR = 21
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _print(*args: Any) -> None:
|
def _print(*args: Any) -> None:
|
||||||
"""Print to stderr, stripping ANSI codes when the stream has no color support.
|
"""Print to stderr, stripping ANSI codes when the stream has no color support.
|
||||||
@@ -408,13 +406,11 @@ async def _log_migration(
|
|||||||
"""
|
"""
|
||||||
registry = kanta._impl.callback_registry
|
registry = kanta._impl.callback_registry
|
||||||
if registry.has("logmigr"):
|
if registry.has("logmigr"):
|
||||||
try:
|
|
||||||
await registry.invoke(
|
await registry.invoke(
|
||||||
"logmigr",
|
"logmigr",
|
||||||
InjectionContext(kanta=kanta, report=result),
|
InjectionContext(kanta=kanta, report=result),
|
||||||
|
on_error=callback_error_reporter("logmigr"),
|
||||||
)
|
)
|
||||||
except Exception:
|
|
||||||
_logger.exception("logmigr callback failed")
|
|
||||||
return
|
return
|
||||||
if quiet:
|
if quiet:
|
||||||
return
|
return
|
||||||
|
|||||||
+32
-2
@@ -54,7 +54,37 @@ def _state_tag(ann: Any) -> str | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
|
|
||||||
|
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)``.
|
||||||
|
"""
|
||||||
|
name = getattr(callback, "__name__", None) or type(callback).__name__
|
||||||
|
doc = inspect.getdoc(callback)
|
||||||
|
if doc:
|
||||||
|
return f"{name} ({doc.splitlines()[0]})"
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def callback_error_reporter(
|
||||||
|
kind: str,
|
||||||
|
) -> 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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _report(callback_error: Exception, callback: Callable[..., Any]) -> None:
|
||||||
|
_logger.exception(
|
||||||
|
"Kanta.%s %s failed: %s", kind, describe_callback(callback), callback_error
|
||||||
|
)
|
||||||
|
|
||||||
|
return _report
|
||||||
|
|
||||||
|
|
||||||
class LogFmt:
|
class LogFmt:
|
||||||
@@ -281,7 +311,7 @@ class CallbackRegistry:
|
|||||||
except Exception:
|
except Exception:
|
||||||
# Formatting must never break functionality; a failing
|
# Formatting must never break functionality; a failing
|
||||||
# callback is reported and treated as a fall-through.
|
# callback is reported and treated as a fall-through.
|
||||||
_logger.exception("logfmt callback %r failed", fn)
|
_logger.exception("Kanta.logfmt %s failed", describe_callback(fn))
|
||||||
continue
|
continue
|
||||||
if resolved is not None:
|
if resolved is not None:
|
||||||
return resolved
|
return resolved
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from kanta.exceptions import FileLockError
|
from kanta.exceptions import FileLockError
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
|
|
||||||
def _fatal(msg: str, *, db_path: Path | None = None) -> None:
|
def _fatal(msg: str, *, db_path: Path | None = None) -> None:
|
||||||
|
|||||||
+3
-8
@@ -10,7 +10,7 @@ from datetime import UTC, datetime, timedelta
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from typing import Any, Generic, TypeVar
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
from kanta.callbacks import CallbackRegistry, InjectionContext
|
from kanta.callbacks import CallbackRegistry, InjectionContext, callback_error_reporter
|
||||||
from kanta.exceptions import DatabaseError, DataIntegrityError, ReplayError
|
from kanta.exceptions import DatabaseError, DataIntegrityError, ReplayError
|
||||||
from kanta.logging import (
|
from kanta.logging import (
|
||||||
_USER_PATH,
|
_USER_PATH,
|
||||||
@@ -25,16 +25,11 @@ from kanta.rotation import execute_rotation, plan_rotation
|
|||||||
from kanta.serialization import restore_data_in_place, struct_to_dict
|
from kanta.serialization import restore_data_in_place, struct_to_dict
|
||||||
from kanta.serialization.base import replay
|
from kanta.serialization.base import replay
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
def _log_callback_error(callback_error, callback):
|
|
||||||
"""Report a failing logging callback and continue with the next one."""
|
|
||||||
_logger.exception("Log callback %r failed: %s", callback, callback_error)
|
|
||||||
|
|
||||||
|
|
||||||
class KantaImpl(PersistenceMixin, Generic[T]):
|
class KantaImpl(PersistenceMixin, Generic[T]):
|
||||||
"""Internal state and logic for Kanta."""
|
"""Internal state and logic for Kanta."""
|
||||||
|
|
||||||
@@ -119,7 +114,7 @@ class KantaImpl(PersistenceMixin, Generic[T]):
|
|||||||
kanta=self._kanta,
|
kanta=self._kanta,
|
||||||
report=report,
|
report=report,
|
||||||
),
|
),
|
||||||
on_error=_log_callback_error,
|
on_error=callback_error_reporter("logmigr"),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
+3
-11
@@ -12,7 +12,7 @@ from datetime import UTC, datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from kanta.callbacks import CallbackRegistry, InjectionContext
|
from kanta.callbacks import CallbackRegistry, InjectionContext, callback_error_reporter
|
||||||
from kanta.diff import diff
|
from kanta.diff import diff
|
||||||
from kanta.exceptions import DatabaseError, DataIntegrityError
|
from kanta.exceptions import DatabaseError, DataIntegrityError
|
||||||
from kanta.filelock import LockedFile
|
from kanta.filelock import LockedFile
|
||||||
@@ -21,7 +21,7 @@ from kanta.serialization import JsonSerializer, Serializer
|
|||||||
from kanta.serialization.framing import Framer
|
from kanta.serialization.framing import Framer
|
||||||
from kanta.snapshot import SnapshotState
|
from kanta.snapshot import SnapshotState
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
|
|
||||||
class PersistenceMixin:
|
class PersistenceMixin:
|
||||||
@@ -110,18 +110,10 @@ class PersistenceMixin:
|
|||||||
break
|
break
|
||||||
except DatabaseError as e:
|
except DatabaseError as e:
|
||||||
self.background_error = e
|
self.background_error = e
|
||||||
|
|
||||||
def _log_callback_error(callback_error, callback):
|
|
||||||
_logger.exception(
|
|
||||||
"Background error callback %r failed: %s",
|
|
||||||
callback,
|
|
||||||
callback_error,
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.callback_registry.invoke(
|
await self.callback_registry.invoke(
|
||||||
"fatal_error",
|
"fatal_error",
|
||||||
InjectionContext(error=e, kanta=self._kanta),
|
InjectionContext(error=e, kanta=self._kanta),
|
||||||
on_error=_log_callback_error,
|
on_error=callback_error_reporter("fatal_error"),
|
||||||
)
|
)
|
||||||
_logger.error("Background flush loop stopped: %s", e)
|
_logger.error("Background flush loop stopped: %s", e)
|
||||||
break
|
break
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ from kanta.structs import ChangeRecord, Snapshot
|
|||||||
from kanta.serialization.base import Serializer, apply_diff
|
from kanta.serialization.base import Serializer, apply_diff
|
||||||
from kanta.serialization.framing import Framer
|
from kanta.serialization.framing import Framer
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ from kanta.structs import Snapshot
|
|||||||
from kanta.serialization import JsonSerializer, Serializer
|
from kanta.serialization import JsonSerializer, Serializer
|
||||||
from kanta.serialization.framing import Framer, LineFramer
|
from kanta.serialization.framing import Framer, LineFramer
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
MINDIFFS = 100
|
MINDIFFS = 100
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ class SnapshotState:
|
|||||||
self._write(file, version, state, ts, m=m)
|
self._write(file, version, state, ts, m=m)
|
||||||
self._force_pending = False
|
self._force_pending = False
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
_logger.error("snapshot: failed to write snapshot: %r", exc)
|
_logger.error("Kanta snapshot failed: %r", exc)
|
||||||
|
|
||||||
def _write(
|
def _write(
|
||||||
self, file, version: int, state: dict, now: datetime, m: datetime | None = None
|
self, file, version: int, state: dict, now: datetime, m: datetime | None = None
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from kanta.callbacks import InjectionContext
|
|||||||
from kanta.logging import _USER_PATH, LogEvent, emit_event, transaction_logger
|
from kanta.logging import _USER_PATH, LogEvent, emit_event, transaction_logger
|
||||||
from kanta.serialization import restore_data_in_place, struct_to_dict
|
from kanta.serialization import restore_data_in_place, struct_to_dict
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger("kanta")
|
||||||
|
|
||||||
|
|
||||||
def _build_logfmt(impl, previous: dict, current: dict):
|
def _build_logfmt(impl, previous: dict, current: dict):
|
||||||
|
|||||||
Reference in New Issue
Block a user