Foxtrot November shared audit-logging library for Django/DRF micro-services
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ftn_audit-1.0.0b4.tar.gz.
File metadata
- Download URL: ftn_audit-1.0.0b4.tar.gz
- Upload date:
- Size: 31.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8c2ac50f1943bfcc46dddc8da33beada7a41d660db304b793e1f616fe5bf4309
|
|
| MD5 |
db3c7d2ebfcde1e2be64b58e229acc30
|
|
| BLAKE2b-256 |
5ddb1f06e4676f5620dd0bf589de9915814ded2749a4a2120cfe9caac3819e48
|
File details
Details for the file ftn_audit-1.0.0b4-py3-none-any.whl.
File metadata
- Download URL: ftn_audit-1.0.0b4-py3-none-any.whl
- Upload date:
- Size: 27.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fc448700da06a1bd0990b0818960f725cfe17c839645ab14f446bd19d59045c
|
|
| MD5 |
1e6dc244f0ca77ce5a0fd5e288200db7
|
|
| BLAKE2b-256 |
386c712abdbf765370286fa2f60c826e14e684650e9f928a853a4a29b16378e7
|