Pre-release
This release is a pre-release and may not be stable for production use.
ftn-audit
Shared Django/DRF audit logging library with asynchronous OTLP delivery via Celery.
What this package includes
- Request and user context capture via middleware (
AuditContextMiddleware) - Late actor hydration in receivers when middleware context has no user yet
- Signal receivers (
pre_save,post_save,post_delete,m2m_changed) - Formatter registry with route-aware and event-aware selection
- Generic formatter fallback when no custom formatter is registered
- Delivery strategies (
NoopAuditStrategy,OtlpAuditStrategy) - Celery task for OTLP emission
- Unit and integration tests with
pytest+pytest-django
Requirements
- Python 3.11+
pip
Minimal Django integration
1) Install app and middleware
# settings.py
INSTALLED_APPS = [
# ...
"ftn_audit.apps.FtnAuditConfig",
]
MIDDLEWARE = [
# ...
"ftn_audit.middleware.AuditContextMiddleware",
]
2) Configure Celery task discovery
# celery.py
from celery import Celery
app = Celery("service_name")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
3) Register formatters in each app
# my_app/audit_formatters.py
from ftn_audit import register_audit_formatter, AbstractAuditLogFormatter
from .models import MyModel
@register_audit_formatter(MyModel)
class MyFormatter(AbstractAuditLogFormatter):
def get_description(self) -> str:
return f"Updated MyModel #{self.subject.pk}"
def get_attributes(self):
attrs = self.get_default_attributes()
attrs["my.attr"] = "value"
return attrs
Formatter resolution order:
(raw_route + event_type)(raw_route)(event_type)(default)
priority is only applied inside the same specificity tier.
4) Recommended settings
AUDIT_ENABLED = True
AUDIT_DELIVERY_MODE = "celery" # celery | sync | noop
AUDIT_STRICT_FORMATTER_VALIDATION = True
AUDIT_AUTO_CONNECT_SIGNALS = True # optional, default True
AUDIT_AUTO_DISCOVER_FORMATTERS = True # optional, default True
AUDIT_CHANGESET_CHECK_RELATIONSHIP = True # optional, include FK changes in changeset
Migration notes
- Foreign-key auditing:
AUDIT_CHANGESET_CHECK_RELATIONSHIPdefaults toTrue. Set it toFalseonly if your service explicitly wants to skip FK changes in update changesets. - Formatter
raw_routecompatibility: middleware now emits canonical placeholders (:name). Update formatterraw_route=registrations from legacy forms (<type:name>,(?P<name>...)) to:name. - Import path rename:
ftn_audit.jobswas renamed toftn_audit.tasks. Update imports, e.g.from ftn_audit.tasks import emit_audit_log_task.
Error handling and safeguards
- Audit code should never break request execution: all middleware/receiver/strategy exceptions are caught and logged.
- Receivers resolve actor as best-effort:
if
AuditContext.user_idis empty, actor fields are hydrated from currentrequest.userwhen available. - Enqueue failures are logged and dropped.
- OTLP emission failures in Celery use bounded retries.
- After max retries, the task logs an error and drops the event.
- OTLP enqueue is registered via
transaction.on_commit, so events are emitted only after commit. - Signal handlers ignore
raw=Truefixture loads to avoid spurious audit events. - Changesets are filtered by
update_fieldswhen present, so only persisted fields are audited. - M2M payload safety:
PK sets are capped (
M2M_PK_SET_MAX) to avoid oversized OTLP payloads. - Celery audit task uses
ignore_result=Trueto avoid unnecessary result-backend writes. - Celery audit task uses late acknowledgements and
reject_on_worker_lost=Trueto improve redelivery on worker crash.
Known limitations
- ContextVars do not cross process boundaries (e.g. Celery workers).
For background jobs, pass actor metadata explicitly and set
AuditContextin task scope. AUDIT_OTLP_FALLBACKis reserved but not currently enforced by runtime behavior.- Django bulk operations bypass per-instance signals (
bulk_create,bulk_update, querysetupdate, querysetdelete). If needed, add explicit auditing at service-layer boundaries. - Reverse-side M2M modifications (
reverse=True) are not audited by default. This can be extended with explicit configuration if your domain needs it. - One-to-many collection semantics should be expressed through child create/update/delete formatters (FK changes). There is no dedicated O2M signal registry yet.
Install
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -e .[test]
Makefile workflow
Bootstrap environment:
make bootstrap
Run tests:
make test
Shortcuts:
make test-unitmake test-integrationmake docker-buildmake docker-test
Test commands
Run all tests:
pytest -q
Run focused unit tests:
pytest -q tests/test_middleware.py tests/test_strategy.py tests/test_receiver_unit.py tests/test_tasks.py
Run integration tests:
pytest -q tests/test_receiver_integration.py
Minimum test coverage checklist
Shared library tests cover:
- autodiscovery imports both
audit_formatters.pymodules andaudit_formatterspackages - middleware canonical
raw_route - registry selection (
raw_routematch -> default -> generic) - M2M receiver firing only for allowlisted collections
- commit safety (
rollbackprevents enqueue) - enqueue failure (
log + drop) - OTLP task retries are bounded
Microservice-level expectations:
- formatter unit tests for key routes
- startup integration test verifies registrations are loaded into registry
Acceptance criteria status
- No explicit audit calls in business logic: yes (signal + middleware driven).
- Audit events include
raw_routeand user metadata when available: yes. - Formatter selection is based on HTTP verb + route template: yes (
raw_route = METHOD|/template). - M2M relations are audited only when allowlisted (
@register_audit_collection): yes. - OTLP emission happens only after commit: yes (
transaction.on_commit). - Audit failures never break the request path: yes (exceptions are swallowed and logged).
Notes
- Tests use minimal Django settings from
tests/settings.py. - Test app is under
tests/test_app. - OTLP network emission is mocked in tests (
emit_audit_log_task.delay).
Docker
Build image:
docker compose build
Run tests in container:
docker compose run --rm test
Metadata
Release files for ftn-audit 1.0.0b4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ftn_audit-1.0.0b4.tar.gz | 31.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ftn_audit-1.0.0b4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.0 kB
Release files / ftn_audit-1.0.0b4.tar.gz
| Download URL | ftn_audit-1.0.0b4.tar.gz |
|---|---|
| Size | 31.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c2ac50f1943bfcc46dddc8da33beada7a41d660db304b793e1f616fe5bf4309
|
|
BLAKE2b-256 checksum How to use checksums |
5ddb1f06e4676f5620dd0bf589de9915814ded2749a4a2120cfe9caac3819e48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / ftn_audit-1.0.0b4-py3-none-any.whl
| Download URL | ftn_audit-1.0.0b4-py3-none-any.whl |
|---|---|
| Size | 27.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4fc448700da06a1bd0990b0818960f725cfe17c839645ab14f446bd19d59045c
|
|
BLAKE2b-256 checksum How to use checksums |
386c712abdbf765370286fa2f60c826e14e684650e9f928a853a4a29b16378e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|