Skip to main content

Structured security and audit logging for Django with OpenTelemetry LogRecord-shaped JSONL output

Project description

django-sec-audit

PyPI version Python versions License GitHub

Structured security and audit logging for Django, emitting OpenTelemetry LogRecord-shaped JSONL events.

Captures HTTP request/response metadata, auth events (login, logout, failures), model changes (via django-auditlog), and DRF view metadata out of the box.

Features

  • HTTP middleware — automatic capture of requests, responses, status codes, timing, client IP, routes, DRF metadata
  • Auth signals — automatic logging of user_logged_in, user_logged_out, user_login_failed
  • Model forwarding — forward django-auditlog entries as structured audit events (optional [model] extra)
  • DRF integration — auto-detects drf_action, drf_view_class, serializer, auth/permission classes (optional [drf] extra)
  • Request body capture — opt-in JSON body capture with scrubbing and size limits
  • OTel JSONL output — every event is a single JSON line following the OTel LogRecord envelope, ready for Loki/Grafana
  • Pluggable pipeline — custom filters and enrichers run before emission

Dependencies

Package Role
django-sec-audit (this) Django integration
sec-audit Core (events, context, IP resolution, scrubbing)
sec-audit-logging Logging runtime, formatters, SIEM handlers

Quick Start

# settings.py

INSTALLED_APPS = [
    'sec_audit.django.apps.SecAuditConfig',   # early, before your apps
    # ...
]

MIDDLEWARE = [
    # ... Django security middleware
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'sec_audit.django.middleware.AuditMiddleware',  # last or near-last
]

SEC_AUDIT = {
    'core': {
        'source': 'myapp',               # appears as resource.service.name
        'log_request_bodies': False,      # opt-in
        'log_ok_responses': False,        # only 4xx/5xx by default
    },
    'logging': {
        'schema_version': '1.0',
    },
    'django': {
        'include_usernames': False,       # opt-in for GDPR considerations
    },
}

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        # Injects the resolved SEC_AUDIT config at construction.
        'audit_jsonl': {
            '()': 'sec_audit.django.logging.formatters.audit_jsonl_formatter',
        },
    },
    'handlers': {
        'audit_stdout': {
            'class': 'logging.StreamHandler',
            'formatter': 'audit_jsonl',
        },
    },
    'loggers': {
        'sec_audit.audit': {
            'handlers': ['audit_stdout'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

Stdout is the supported delivery path (stdout JSONL -> Grafana Alloy -> Loki). For local file output, swap the handler for a stdlib logging.handlers.RotatingFileHandler using the same audit_jsonl formatter.

Usage Guide

For a step-by-step setup and configuration walkthrough, see docs/how-to-use.md.

Monitoring with Loki/Grafana

Audit events are OTel JSONL, ready to ship to Loki (sec_audit.audit JSONL → Grafana Alloy → Loki → Grafana). The bundled sec-audit-loki-init command generates the whole local stack — see docs/loki-setup.md.

Configuration

All settings live under the SEC_AUDIT dict in settings.py. Three sections are recognised:

core (CoreAuditConfig)

Setting Default Description
source 'sec-audit' Service name in resource.service.name
ignore_paths () Regex patterns; matching paths are skipped
ignore_status_codes frozenset() Status code values to skip (e.g. {301, 302})
sample_rate 1.0 Sampling rate for successful (2xx) responses
log_ok_responses False Enable logging for 2xx responses
log_request_bodies False Enable request body capture
log_body_paths () Regex patterns; only matching paths capture bodies
body_methods {'POST', 'PUT', 'PATCH'} HTTP methods eligible for body capture
max_body_bytes 4096 Maximum JSON body size in bytes
sensitive_keys DEFAULT_SENSITIVE_KEYS Built-in key patterns to scrub (incl. password, secret, token, apikey, etc.)
sensitive_key_allowlist () Exact (compacted) field names never redacted, even when a sensitive_keys substring matches — e.g. credit_card_last4, token_expiry
sensitive_value_patterns () Regex patterns matching sensitive values to scrub

logging (LoggingAuditConfig)

Setting Default Description
schema_version '1.0' Schema version string in every event
projection_limits ProjectionLimits() Bounds for dict/list nesting and string sizes (accepts a dict or ProjectionLimits)

File-rotation parameters (maxBytes/backupCount/filename) are configured on the handler in Django's LOGGING dict, not here.

django (DjangoAuditConfig)

Setting Default Description
include_usernames False Include user.name in events (opt-in for GDPR)
trusted_proxy_cidrs () CIDR ranges considered trusted proxies
trusted_proxy_count None Number of trusted proxies (leftmost N IPs are strip)
emit_session_id False Emit correlated session.id in events (opt-in; writes to request.session)
filters () Dotted paths to filter callable/classes
enrichers () Dotted paths to enricher callable/classes

Event Types

auth.login.success        auth.login.failed
auth.logout.success       auth.logout.unknown
http.response.success     http.response.client_error   http.response.server_error
model.create              model.update                 model.delete         model.access

Output Format

Each event is a single JSON line:

{
  "timestamp": 1712345678000000000,
  "observed_timestamp": 1712345678000000000,
  "severity_text": "WARNING",
  "severity_number": 13,
  "body": "http.response",
  "resource": { "service.name": "sec-audit" },
  "instrumentation_scope": { "name": "sec_audit.django.middleware", "version": "1.0.0" },
  "attributes": {
    "event_type": "http.response.client_error",
    "source.address": "203.0.113.10",
    "http.request.method": "POST",
    "http.response.status_code": 404,
    "url.full": "https://example.test/api/transfer",
    "url.path": "/api/transfer",
    "user.id": "42"
  },
  "event_name": "http.response.client_error"
}

Optional Extras

Extra Dependencies
pip install django-sec-audit[model] django-auditlog
pip install django-sec-audit[drf] djangorestframework
pip install django-sec-audit[full] Both

Development

pip install -e "packages/django-sec-audit[dev]"
pytest
ruff check .

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_sec_audit-0.1.0a4.tar.gz (41.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_sec_audit-0.1.0a4-py3-none-any.whl (29.0 kB view details)

Uploaded Python 3

File details

Details for the file django_sec_audit-0.1.0a4.tar.gz.

File metadata

  • Download URL: django_sec_audit-0.1.0a4.tar.gz
  • Upload date:
  • Size: 41.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for django_sec_audit-0.1.0a4.tar.gz
Algorithm Hash digest
SHA256 3d46581da36b0dcd3c814d3574871f148ce6c59be577a1ad06f2b138589e365d
MD5 d7c41bc26a0ffe525cf1b445aa2d3eee
BLAKE2b-256 10c45e9bffc6e3e2f4bb5c41f9cb5b5fa976f167e8810a0ae9de89b3970f8d16

See more details on using hashes here.

File details

Details for the file django_sec_audit-0.1.0a4-py3-none-any.whl.

File metadata

File hashes

Hashes for django_sec_audit-0.1.0a4-py3-none-any.whl
Algorithm Hash digest
SHA256 d302e53a770cb81c1419b684920e18d22500bd25f53debcc834f22700ed6fc3f
MD5 6ff385628b0d09d59dc407f5df3d39c1
BLAKE2b-256 a7cb034b94bc895fee1b357b4f61c859bf8c4c6ca697116807be188cd7ef2584

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page