audit_trail.writer¶
Inserts into the audit tables, in-session and durable.
Everything here works on a Core Connection and never touches the ORM, so
it is safe to call from after_flush.
A write in the caller's transaction is one audit_transaction row per
database transaction (inserted by the first entry, then reused) and one
INSERT ... executemany of audit_activity rows per call. Rows are
never updated afterwards.
A durable write runs on the library's own connection, in its own transaction
holding one audit_transaction row and its activity rows, committed at
once and never through the caller's session.
CHECK_VIOLATION
module-attribute
¶
SQLSTATE of a row that fits no partition (and of other CHECK violations).
DURABLE_WRITE_ERRORS
module-attribute
¶
Failures of a durable write that the write policy handles.
AuditWriteError ¶
Bases: Exception
A durable audit write failed and the write policy says to raise.
Raised for fail_closed events, and for other durable writes with
on_error="raise". The original error (a DBAPIError, the pool's
sqlalchemy.exc.TimeoutError or a PartitionError) is the
__cause__. Nothing of the entry was committed.
TransactionRow ¶
Bases: NamedTuple
The audit_transaction row the entries of one transaction share.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
int
|
The row's |
issued_at |
datetime
|
The row's |
correlation_id |
UUID | None
|
The row's |
TransactionValues ¶
Bases: _TransactionColumns
Column values of an audit_transaction row.
meta is left out when the context has no extra, so the column
stays SQL NULL rather than JSON null.
Entry ¶
Bases: TypedDict
One activity row before it is attached to a transaction row.
encode_meta ¶
encode_meta(
extra: Mapping[str, object],
json_encoder: type[JSONEncoder] | None,
) -> dict[str, object]
Encode AuditContext.extra for a JSON column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extra
|
Mapping[str, object]
|
The host's extra context. |
required |
json_encoder
|
type[JSONEncoder] | None
|
Host encoder for types |
required |
Returns:
| Type | Description |
|---|---|
dict[str, object]
|
JSON-native data. |
Raises:
| Type | Description |
|---|---|
UnserializableValueError
|
A value cannot be encoded. |
Source code in audit_trail/writer.py
context_data ¶
Return the data.context snapshot of ctx with meta encoded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
AuditContext
|
The context of the entry. |
required |
json_encoder
|
type[JSONEncoder] | None
|
Host encoder for |
required |
Returns:
| Type | Description |
|---|---|
ContextSnapshot
|
The snapshot, ready for a JSON column. |
Raises:
| Type | Description |
|---|---|
UnserializableValueError
|
An |
Source code in audit_trail/writer.py
encode_payload ¶
encode_payload(
payload: dict[str, object] | BaseModel | None,
*,
keys: KeyRing | None,
json_encoder: type[JSONEncoder] | None,
) -> dict[str, JSONValue]
Encode a validated event payload for data["payload"].
A pydantic model is dumped (Python mode) and its Pseudonymized fields
are replaced with pseudonymize(value, purpose=<field name>); None
stays None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload
|
dict[str, object] | BaseModel | None
|
The result of |
required |
keys
|
KeyRing | None
|
Key ring for |
required |
json_encoder
|
type[JSONEncoder] | None
|
Host encoder for types |
required |
Returns:
| Type | Description |
|---|---|
dict[str, JSONValue]
|
JSON-native data; |
Raises:
| Type | Description |
|---|---|
PayloadError
|
A |
ValueError
|
A |
UnserializableValueError
|
A value cannot be encoded. |
Source code in audit_trail/writer.py
transaction_values ¶
transaction_values(
ctx: AuditContext,
json_encoder: type[JSONEncoder] | None,
) -> TransactionValues
Build the audit_transaction row for a context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
AuditContext
|
The context when the row is created. |
required |
json_encoder
|
type[JSONEncoder] | None
|
Host encoder for |
required |
Returns:
| Type | Description |
|---|---|
TransactionValues
|
The column values. |
Raises:
| Type | Description |
|---|---|
UnserializableValueError
|
An |
Source code in audit_trail/writer.py
insert_transaction ¶
insert_transaction(
connection: Connection,
table: Table,
values: TransactionValues,
) -> TransactionRow
Insert an audit_transaction row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
Connection in the transaction being audited. |
required |
table
|
Table
|
The |
required |
values
|
TransactionValues
|
The row, from |
required |
Returns:
| Type | Description |
|---|---|
TransactionRow
|
The inserted row's |
Source code in audit_trail/writer.py
insert_activities ¶
insert_activities(
connection: Connection,
table: Table,
transaction: TransactionRow,
entries: Sequence[Entry],
) -> None
Insert activity rows with one executemany.
Every row gets the transaction row's id, issued_at (as
created_at) and correlation_id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
Connection in the transaction being audited. |
required |
table
|
Table
|
The |
required |
transaction
|
TransactionRow
|
The transaction row the entries belong to. |
required |
entries
|
Sequence[Entry]
|
The entries; nothing is executed when empty. |
required |
Source code in audit_trail/writer.py
run_isolated ¶
Run an audit write according to the on_error policy.
"raise" runs work directly; any error propagates and fails the
caller's transaction. "log" runs it inside a savepoint on the
connection (never a Session savepoint, which would snapshot and later
restore the unit of work in the middle of a flush). A DBAPIError rolls
the savepoint back, is logged and swallowed, so the caller's transaction
continues without the audit rows. A missing partition (SQLSTATE
23514) is logged as such; it is not retried. Other exceptions always
propagate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
Connection in the transaction being audited. |
required |
on_error
|
OnError
|
The write policy. |
required |
work
|
Callable[[], _T]
|
The inserts to run. |
required |
Returns:
| Type | Description |
|---|---|
_T | None
|
What |
Source code in audit_trail/writer.py
is_missing_partition ¶
Tell whether a database error is a row that fits no partition.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc
|
DBAPIError
|
The error. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
relation" message. |
Source code in audit_trail/writer.py
write_durable ¶
write_durable(
connection: Connection,
tables: AuditTables,
severities: Sequence[int],
values: TransactionValues,
entries: Sequence[Entry],
*,
auto_create_partitions: bool,
) -> TransactionRow
Write one audit_transaction row and its entries, and commit.
Runs its own transactions on connection, which must be idle and not
in AUTOCOMMIT mode. With auto_create_partitions, a row that fits
no partition (SQLSTATE 23514) makes it create the missing partitions
with ensure_partitions in a separate transaction on the same
connection and retry once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection
|
Connection
|
A connection of the durable engine. |
required |
tables
|
AuditTables
|
The audit tables. |
required |
severities
|
Sequence[int]
|
Severity values to create partitions for on a retry. |
required |
values
|
TransactionValues
|
The |
required |
entries
|
Sequence[Entry]
|
The entries. |
required |
auto_create_partitions
|
bool
|
Create missing partitions and retry once. |
required |
Returns:
| Type | Description |
|---|---|
TransactionRow
|
The committed transaction row. |
Raises:
| Type | Description |
|---|---|
DBAPIError
|
An insert failed (on the retry, when there was one). |
PartitionError
|
Creating the partitions failed, for example
|
Source code in audit_trail/writer.py
handle_durable_failure ¶
handle_durable_failure(
exc: DBAPIError | TimeoutError | PartitionError,
on_error: OnError,
fail_closed: bool,
) -> None
Apply the write policy to a failed durable write.
A fail_closed write, or any durable write with on_error="raise",
raises AuditWriteError; otherwise the failure is logged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc
|
DBAPIError | TimeoutError | PartitionError
|
The failure, one of |
required |
on_error
|
OnError
|
The write policy. |
required |
fail_closed
|
bool
|
The entry must not be lost; always raise. |
required |
Raises:
| Type | Description |
|---|---|
AuditWriteError
|
The policy says to raise; |