Route transaction aborts through logemit as 'aborted' events

The rollback warning is now a LogEvent (kind='aborted', WARNING level,
carrying the exception) dispatched via emit_event so logemit callbacks can
handle or restyle it. Default rendering: action in transaction color
without quotes, followed by ' transaction aborted: {exc}' in default
color.
This commit is contained in:
Leo Vasanko
2026-08-07 05:40:01 +00:00
parent f131f72749
commit 79faa220df
4 changed files with 62 additions and 16 deletions
+12 -11
View File
@@ -225,16 +225,17 @@ def resolve_user_key(value: str) -> str | None:
#### Log Emitters
- Every change-related message Kanta emits (transaction/bootstrap/migration
changes, `Created <file>`, migration summaries) is described by a
`kanta.logging.LogEvent` and dispatched through `kanta.logging.emit_event`.
Kanta's own output goes through the same mechanism: when no `logemit`
callback handles an event, `kanta.logging.default_emit` renders it with the
built-in formatting.
changes, `Created <file>`, migration summaries, aborted transactions) is
described by a `kanta.logging.LogEvent` and dispatched through
`kanta.logging.emit_event`. Kanta's own output goes through the same
mechanism: when no `logemit` callback handles an event,
`kanta.logging.default_emit` renders it with the built-in formatting.
- A `LogEvent` carries the event `kind` (`"change"`, `"created"`,
`"migrated"`), the preferred `logger` and `level`, and all relevant state:
`action`, `user`, `extra`, `diff`, `previous`/`current` state dicts, the
built `logfmt` chain, and version info for migration events. Pretty
`header` and `diff_lines` are lazy properties, built only if accessed.
`"migrated"`, `"aborted"`), the preferred `logger` and `level`, and all
relevant state: `action`, `user`, `extra`, `error` (for aborted
transactions), `diff`, `previous`/`current` state dicts, the built `logfmt`
chain, and version info for migration events. Pretty `header` and
`diff_lines` are lazy properties, built only if accessed.
- `@kanta.logemit` registers a callback receiving the event. The callback
decides what is logged and where: it may log one or more messages on
`event.logger`, log somewhere else, or nothing at all. A falsy return
@@ -242,8 +243,8 @@ def resolve_user_key(value: str) -> str | None:
passes the event — possibly modified — to the next registered callback.
When all callbacks pass, `default_emit` renders the event; a callback may
also call `default_emit(event)` itself to delegate events it does not
customize. Operational diagnostics (rollback warnings, integrity errors)
do not go through this mechanism.
customize. Operational diagnostics (integrity errors, background flush
failures) do not go through this mechanism.
- Logging never breaks functionality: a crashing `logemit` callback is
reported with `logger.exception` and the event falls back to the built-in
formatting; if the built-in formatting itself fails, the error is reported