audit_trail.config¶
Library configuration: AuditTrail and per-model AuditOptions.
DURABLE_POOL_SIZE
module-attribute
¶
Connections in the durable engine the library builds.
DURABLE_POOL_TIMEOUT
module-attribute
¶
Seconds a durable write waits for a connection of that engine.
Target ¶
Bases: NamedTuple
A parent object referred to by type name and primary key.
Returned by the target option and accepted by AuditTrail.log.
A plain (type, id) tuple works too.
Attributes:
| Name | Type | Description |
|---|---|---|
type |
str
|
Name stored in |
id |
object
|
The primary key; a tuple for a composite key. |
AuditOptions
dataclass
¶
AuditOptions(
severity: IntEnum | None = None,
verb_severity: dict[str, IntEnum] = dict(),
track_relationships: Collection[str] = frozenset(),
snapshot_on_load: Collection[str] = frozenset(),
object_type: str | None = None,
label: Callable[[Any], str | None] | None = None,
scope: Callable[[Any], object] | None = None,
target: Callable[
[Any], Target | tuple[str, object] | None
]
| None = None,
)
Audit settings for one model, set as __audit__ on the class.
label, scope and target run while the session flushes and get
a read-only view of the instance instead of the instance itself. They may
read only attributes already loaded on it: reading an expired or unloaded
attribute emits no SQL, logs a warning on audit_trail.diff (once per
model, attribute and option) and the option falls back (label and
target to None, scope to the context). A column that was
never set on a new instance reads as None, as stored, unless it has a
server-side default. Reading a
functools.cached_property raises AuditOptionError. Any other
exception they raise propagates and aborts the flush.
Attributes:
| Name | Type | Description |
|---|---|---|
severity |
IntEnum | None
|
Severity of the model's |
verb_severity |
dict[str, IntEnum]
|
Per-verb severity overrides, keyed by verb
(for example |
track_relationships |
Collection[str]
|
Relationship names whose membership changes are
recorded as |
snapshot_on_load |
Collection[str]
|
JSON columns deep-copied on load and refresh, so an in-place mutation still has an old value. |
object_type |
str | None
|
Name stored in |
label |
Callable[[Any], str | None] | None
|
Returns the object's label, stored in |
scope |
Callable[[Any], object] | None
|
Returns the scope id, stored in |
target |
Callable[[Any], Target | tuple[str, object] | None] | None
|
Returns the parent object as a |
AuditTrail ¶
AuditTrail(
engine: Engine | AsyncEngine,
*,
schema: str = "audit",
severities: type[IntEnum] | None = None,
default_severity: IntEnum | None = None,
system_severity: IntEnum | None = None,
events: Iterable[type[AuditEvent]] | None = None,
durable_engine: Engine | AsyncEngine | None = None,
on_error: OnError = "log",
pseudonymize_key: bytes
| dict[int, bytes]
| None = None,
session_provider: Callable[[], Any] | None = None,
context_provider: Callable[[], Any] | None = None,
json_encoder: type[JSONEncoder] | None = None,
indexes: Collection[str] | None = None,
global_redact: Collection[str] = (),
warn_on_bulk: bool = True,
auto_create_partitions: bool = False,
allow_scrub: bool = False,
)
Entry point of the library: configuration plus the public operations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
Engine | AsyncEngine
|
Sync or async engine used for DDL and maintenance. |
required |
schema
|
str
|
Database schema holding the audit tables. |
'audit'
|
severities
|
type[IntEnum] | None
|
The application's severity |
None
|
default_severity
|
IntEnum | None
|
Severity of |
None
|
system_severity
|
IntEnum | None
|
Severity of the library's own |
None
|
events
|
Iterable[type[AuditEvent]] | None
|
Event classes to register. |
None
|
durable_engine
|
Engine | AsyncEngine | None
|
Engine for durable writes: sync for |
None
|
on_error
|
OnError
|
|
'log'
|
pseudonymize_key
|
bytes | dict[int, bytes] | None
|
HMAC key as |
None
|
session_provider
|
Callable[[], Any] | None
|
Returns the current session, for hosts that keep it in a context variable. |
None
|
context_provider
|
Callable[[], Any] | None
|
Returns the current audit context. |
None
|
json_encoder
|
type[JSONEncoder] | None
|
Extra encoder for types the built-in one does not handle. |
None
|
indexes
|
Collection[str] | None
|
Names of the indexes to create. |
None
|
global_redact
|
Collection[str]
|
Attribute keys or column names redacted in
|
()
|
warn_on_bulk
|
bool
|
Log a warning when a bulk |
True
|
auto_create_partitions
|
bool
|
Let durable writes create a missing partition and retry once. Needs DDL privileges. |
False
|
allow_scrub
|
bool
|
Enable |
False
|
Attributes:
| Name | Type | Description |
|---|---|---|
severities |
type[IntEnum]
|
The severity enum in use. |
registry |
The registered events. |
|
tables |
The audit tables built from |
|
keys |
Key ring built from |
|
maintenance |
Partition management on |
|
query |
Read queries, for example |
|
durable_engine |
Engine | AsyncEngine
|
The engine durable writes use. |
Raises:
| Type | Description |
|---|---|
EventRegistryError
|
An event or a severity setting is invalid. |
ValueError
|
A table name or an index key is invalid, or
|
TypeError
|
|
Source code in audit_trail/config.py
install ¶
install(
session_factory: type[Session | AsyncSession]
| sessionmaker[Any]
| async_sessionmaker[Any],
) -> None
Audit the sessions of a factory.
Registers the listeners on the factory's session class only (for a
sessionmaker, the class it builds), so other sessions, a second
sessionmaker and tools such as Alembic are not audited. Set
session.info["audit_enabled"] = False to switch capture off for
one session of that class.
An AsyncSession runs a sync Session in a greenlet, and the
listeners are registered on that class: give the async factory your
own Session subclass, as in
async_sessionmaker(engine, sync_session_class=AppSession) or as
sync_session_class of an AsyncSession subclass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_factory
|
type[Session | AsyncSession] | sessionmaker[Any] | async_sessionmaker[Any]
|
A |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
|
ValueError
|
The session class is the base |
Source code in audit_trail/config.py
pseudonymize ¶
Replace a value with a keyed pseudonym, e.g. audit.<purpose>.v1:<hex>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value
|
object
|
The value. Must not be |
required |
purpose
|
str
|
Snake_case name separating unrelated uses. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The pseudonym token. |
Raises:
| Type | Description |
|---|---|
ValueError
|
No |
TypeError
|
|
UnserializableValueError
|
|
Source code in audit_trail/config.py
dispose ¶
Close the pooled connections of a durable engine the library built.
A durable_engine passed in is the host's to dispose; this leaves it
alone.
Raises:
| Type | Description |
|---|---|
TypeError
|
The library's durable engine is async; use |
Source code in audit_trail/config.py
adispose
async
¶
Async dispose, for a durable engine the library built async.
Raises:
| Type | Description |
|---|---|
TypeError
|
The library's durable engine is sync; use |
Source code in audit_trail/config.py
log ¶
log(
session: Session,
event: AuditEvent,
*,
obj: object | None = None,
target: Target | object | None = None,
payload: Mapping[str, object] | BaseModel | None = None,
actor: Actor | None = None,
durable: bool | None = None,
) -> None
Record an explicit event.
A non-durable entry is inserted immediately on
session.connection(), in the same database transaction as the
session's changes: it is committed or rolled back with them, and
shares their audit_transaction row. Write failures follow
on_error.
A durable entry (the event's durable or fail_closed, or
durable=True) is written on a connection of durable_engine in
a transaction of its own, with its own audit_transaction row built
from the current context, and committed before this returns, so it
survives a rollback of the session. A failed write is logged with
on_error="log"; with on_error="raise", and always for a
fail_closed event, it raises AuditWriteError. With
auto_create_partitions, a missing partition is created and the
write retried once. Creating a partition waits for every open
transaction that has written audit rows, including the session's own:
if it has, the retry fails after the ensure_partitions lock
timeout (about 5 to 10 seconds) and a fail_closed event raises
AuditWriteError. Run ensure_partitions ahead of time rather
than relying on the retry.
Either kind is written even when session.info["audit_enabled"] is
False, which only switches off the automatic entity.* entries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
A session of a class this |
required |
event
|
AuditEvent
|
A registered host event. |
required |
obj
|
object | None
|
The object the event is about; sets |
None
|
target
|
Target | object | None
|
The parent object, as an instance or a |
None
|
payload
|
Mapping[str, object] | BaseModel | None
|
The payload, validated against the event's schema.
|
None
|
actor
|
Actor | None
|
The actor of this entry. It sets the entry's |
None
|
durable
|
bool | None
|
Overrides the event's |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
The session's class is not installed by this
|
ValueError
|
The event uses a reserved prefix, |
UnknownEventError
|
The event is not registered. |
PayloadError
|
The payload does not match the event's schema, or a
|
AuditWriteError
|
A durable write failed and the policy says to raise. |
Source code in audit_trail/config.py
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | |
alog
async
¶
alog(
session: AsyncSession,
event: AuditEvent,
*,
obj: object | None = None,
target: Target | object | None = None,
payload: Mapping[str, object] | BaseModel | None = None,
actor: Actor | None = None,
durable: bool | None = None,
) -> None
Async log, for an AsyncSession of an installed factory.
A non-durable entry is written in the session's transaction, a
durable one on the async durable_engine; see log.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
AsyncSession
|
An |
required |
event
|
AuditEvent
|
A registered host event. |
required |
obj
|
object | None
|
The object the event is about; see |
None
|
target
|
Target | object | None
|
The parent object; see |
None
|
payload
|
Mapping[str, object] | BaseModel | None
|
The payload; see |
None
|
actor
|
Actor | None
|
The actor of this entry; see |
None
|
durable
|
bool | None
|
Overrides the event's |
None
|
Raises:
| Type | Description |
|---|---|
TypeError
|
The session's class is not installed by this
|
ValueError
|
As for |
UnknownEventError
|
The event is not registered. |
PayloadError
|
The payload does not match the event's schema. |
AuditWriteError
|
A durable write failed and the policy says to raise. |