67 lines
1.7 KiB
Python
67 lines
1.7 KiB
Python
"""Single entry point for writing :class:`AuditLog` rows.
|
|
|
|
``log()`` is fire-and-forget: it awaits the DB write so callers get
|
|
ordering, but never raises — failures are logged and swallowed.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from beaver_gateway.storage import append_audit
|
|
|
|
if TYPE_CHECKING:
|
|
from beaver_gateway.frontends.base import GatewayRuntime
|
|
|
|
|
|
_log = logging.getLogger("beaver_gateway.audit")
|
|
|
|
|
|
KNOWN_KINDS: frozenset[str] = frozenset(
|
|
{
|
|
"messages",
|
|
"mcp_call",
|
|
"tool_call",
|
|
"login_ok",
|
|
"login_failed",
|
|
"logout",
|
|
"token_create",
|
|
"token_revoke",
|
|
}
|
|
)
|
|
"""Kinds the gateway currently emits; ``AuditLog.kind`` stays free-form."""
|
|
|
|
|
|
async def log(
|
|
runtime: GatewayRuntime,
|
|
*,
|
|
actor: str,
|
|
kind: str,
|
|
agent_name: str | None = None,
|
|
**detail: Any,
|
|
) -> None:
|
|
"""Best-effort audit insert. Never raises; DB failures are logged.
|
|
|
|
``actor`` is free-form (``"token:<name>"``, ``"admin:<user>"``,
|
|
``"anon"``); ``kind`` is a short tag (see :data:`KNOWN_KINDS`); and
|
|
``**detail`` is JSON-serialised into ``AuditLog.detail_json`` — keep
|
|
it small, not full request bodies.
|
|
"""
|
|
try:
|
|
async with runtime.db.session() as session:
|
|
await append_audit(
|
|
session,
|
|
actor=actor,
|
|
kind=kind,
|
|
agent_name=agent_name,
|
|
detail=detail or None,
|
|
)
|
|
except Exception: # noqa: BLE001
|
|
_log.exception(
|
|
"audit write failed: actor=%s kind=%s agent=%s", actor, kind, agent_name
|
|
)
|
|
|
|
|
|
__all__ = ["KNOWN_KINDS", "log"]
|