A reusable Django DRF application for auditing requests, logins, and custom processes.
- HTTP request auditing (
RequestAuditEvent) - Login and logout auditing (
LoginAuditEvent) - Custom process auditing (
ProcessAuditEvent,StepAuditEvent,RegistrationAuditEvent) - Integration with SimpleJWT
- Django Async support
- Thread safe
- Error and stacktrace tracking
- PDF report generation
pip install drf-audit-trailIn your settings.py:
INSTALLED_APPS = [
...
"drf_audit_trail",
]
MIDDLEWARE = [
...
"drf_audit_trail.middleware.RequestLoginAuditEventMiddleware",
]You can use a separate database for audit data:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
},
"audit_trail": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "audit_trail.sqlite3",
},
}
DRF_AUDIT_TRAIL_DATABASE_ALIAS = "audit_trail" # Audit database alias
DJANO_DEFAULT_DATABASE_ALIAS = "default" # Default database alias
DATABASE_ROUTERS = ["drf_audit_trail.database_router.DRFAuditTrail"]Add to your settings.py as needed:
DRF_AUDIT_TRAIL_REQUEST_AUDIT_URLS = [r"^(?!/admin/jsi18n/).*$"] # Monitored URLs (regex)
DRF_AUDIT_TRAIL_AUTH_URL = [
"/api/token/",
"/admin/login/",
"/api/logout/",
"/admin/logout/",
] # Authentication endpoints
DRF_AUDIT_TRAIL_AUTH_STATUS_CODE_FAILED = 401 # Auth failure status code
DRF_AUDIT_TRAIL_NOTSAVE_REQUEST_BODY_URLS = ['/api/token'] # Endpoints that do not save request body
DRF_AUDIT_TRAIL_USER_PK_NAME = "pk" # User PK field nameAll settings are optional and have sensible defaults.
- RequestAuditEvent: HTTP request auditing.
- LoginAuditEvent: Login/logout auditing.
- ProcessAuditEvent: Custom process auditing.
- StepAuditEvent: Process step auditing.
- RegistrationAuditEvent: Execution registration for each step.
from rest_framework.views import APIView
from rest_framework.response import Response
class TestAPIView(APIView):
def get(self, request, *args, **kwargs):
drf_request_audit_event = request.META.get("drf_request_audit_event")
drf_request_audit_event["extra_informations"] = {
"data": "Example of extra information"
}
return Response("ok")To audit custom business processes, use the process audit utilities:
from drf_audit_trail.models import (
ProcessAuditEvent,
RegistrationAuditEvent,
StepAuditEvent,
)
from drf_audit_trail.process_audit import ProcessAudit
class CreateProductProcessAudit(ProcessAudit):
def create_process(self) -> ProcessAuditEvent:
return self.save_model(ProcessAuditEvent(name="Criar produto"))
def create_steps(self, process: ProcessAuditEvent):
self.step_validation = self.save_model(
StepAuditEvent(
name="Validação dos Dados",
order=1,
process=process,
total_registrations=2,
)
)
self.step_save_db = self.save_model(
StepAuditEvent(
name="Salvar no banco de dados",
order=2,
process=process,
)
)
def create_registration_step_validation_code(
self, success, name=None, **extra_fields
):
name = name or "Codigo do produto validados com sucesso"
return self.save_model(
RegistrationAuditEvent(
name=name, step=self.step_validation, success=success, **extra_fields
)
)
def create_registration_step_validation(self, success, name=None, **extra_fields):
name = name or "Dados de criação validados com sucesso"
return self.save_model(
RegistrationAuditEvent(
name=name, step=self.step_validation, success=success, **extra_fields
)
)
def create_registration_save_db(self, success, name=None, **extra_fields):
name = name or "Salvar no banco de dados"
return self.save_model(
RegistrationAuditEvent(
step=self.step_save_db, success=success, name=name, **extra_fields
)
)
class ProductViewSet(ModelViewSet):
serializer_class = ProductSerializer
queryset = Product.objects.all()
def create(self, request, *args, **kwargs):
process_audit = CreateProductProcessAudit(request)
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
process_audit.create_registration_step_validation_code(True)
process_audit.create_registration_step_validation(True)
else:
if serializer.errors.get("code") is not None:
process_audit.create_registration_step_validation_code(
False,
"Error de validação de codigo",
description=json.dumps(serializer.errors.get("code")),
)
validation_errors = json.dumps(serializer.errors)
process_audit.create_registration_step_validation(
False, "Erros de validação", description=validation_errors
)
raise ValidationError(serializer.errors)
try:
self.perform_create(serializer)
process_audit.create_registration_save_db(True)
except BaseException as e:
process_audit.create_registration_save_db(False, e.__str__())
raise
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=201, headers=headers)Starting from version X.X.X, DRF Audit Trail automatically prevents DataError (e.g., StringDataRightTruncation) when saving audit events, even when request parameters (like URLs or query strings) exceed the database limit.
- Fields sensitive to length, such as
urlandquery_paramsin theRequestAuditEventmodel, use a custom field that automatically truncates values exceeding the database limit (e.g., 2048 characters). - When truncation occurs, a warning is logged via Python (
drf_audit_trail.truncation), enabling traceability. - This ensures the audit middleware never causes a request to fail due to oversized data, making the solution robust for public APIs or endpoints with extensive parameters.
WARNING drf_audit_trail.truncation: Truncating value for field 'url' to 2048 characters. Original length: 3010.
- Truncation is transparent to the library user.
- To audit this behavior, set the log level to
WARNINGin thedrf_audit_trail.truncationlogger. - This behavior applies to all fields of type
TruncatingCharField.
MIT License
- All settings are optional and have default values.
- For advanced customization, see the code and docstrings.
- For questions, check the docstrings or open an issue.


