Audit Logging¶
Audit logging captures every permission decision made by the system. General Manager plugs audit loggers via the AUDIT_LOGGER setting, and ships with two ready-to-use implementations. During AppConfig.ready the package reads GENERAL_MANAGER['AUDIT_LOGGER'] (or the top-level AUDIT_LOGGER) and automatically wires the logger; when unset, logging is disabled with zero overhead.
Configuration via settings¶
# settings.py
GENERAL_MANAGER = {
"AUDIT_LOGGER": "general_manager.permission.audit.DatabaseAuditLogger",
}
Pass either the dotted import path of a logger class/instance or a mapping:
GENERAL_MANAGER = {
"AUDIT_LOGGER": {
"class": "general_manager.permission.audit.DatabaseAuditLogger",
"options": {
"using": "replica", # database alias
"table_name": "gm_permission_audit",
"batch_size": 500,
"flush_interval": 0.25,
},
}
}
PermissionAuditEvent¶
Each audit logger receives a PermissionAuditEvent instance containing:
action– one ofcreate,read,update,delete, ormutation.attributes– attributes evaluated during the check.granted/bypassed– decision outcome, including superuser shortcuts.manager– name of the permissioned manager, if available.user_id/user_repr– primary key when available or best-effort string representation.permissions– evaluated expressions (e.g.,isAdmin).metadata– custom context supplied by loggers or future extensions.
Built-in loggers¶
FileAuditLogger¶
Streams events as newline-delimited JSON to the given path. Uses a background worker with batching to keep request latency low.
FileAuditLogger writes newline-delimited JSON to the given path using a background worker. When configuring imperatively, call configure_audit_logger.
DatabaseAuditLogger persists events via Django’s ORM (default table: general_manager_permissionauditlog). The logger creates the table on demand; on SQLite it falls back to synchronous writes for compatibility with in-memory tests. Use using to target a different database or table_name for custom storage.
# apps.py
from django.apps import AppConfig
from general_manager.permission import configure_audit_logger
from general_manager.permission import DatabaseAuditLogger, FileAuditLogger
class GeneralManagerIntegrationConfig(AppConfig):
name = "project.general_manager_integration"
def ready(self) -> None:
configure_audit_logger(DatabaseAuditLogger())
# or configure_audit_logger(FileAuditLogger("/var/log/general-manager-audit.log"))
Custom loggers¶
Logger options¶
FileAuditLoggeracceptsbatch_sizeandflush_interval(seconds) besides the file path.DatabaseAuditLoggeracceptsusing(database alias),table_name(audit table name),batch_size(events per bulk insert) andflush_interval(seconds between background flushes).
Implement the AuditLogger protocol (record(event: PermissionAuditEvent) -> None) and register the logger via configure_audit_logger() or the settings hook. For asynchronous pipelines (Kafka, Celery, …) batch events to minimise request overhead.