Deduplicate identical Django admin error emails in short bursts (like frontend retry storms) so operators receive signal, not spam.
- Drop-in replacement for Django's
AdminEmailHandler - Suppresses duplicates of the same underlying error within a configurable time window, even when they come from different requests/users (dedup is keyed on exception type + message + originating source line, not on the full email body, so per-request details like client IP or query params don't defeat it)
- Sends summary emails for suppressed duplicates
- Works across multiple workers when they share a state file path
- Falls back to in-process dedup state if shared file state is unavailable (a warning is logged when this happens)
- Never lets a handler failure break the request that triggered it (errors during dedup/email-building are routed through the standard
logging.Handler.handleError, matching Django's ownAdminEmailHandlerbehavior)
pip install django-error-mail-dedupIn Django LOGGING settings:
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"filters": {
"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"},
},
"handlers": {
"mail_admins": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django_error_mail_dedup.handlers.DedupAdminEmailHandler",
"dedup_window_seconds": 180,
"summary_email_every_seconds": 60,
"state_file_path": "/var/log/myapp/error_email_dedup_state.json",
}
},
"loggers": {
"django.request": {
"handlers": ["mail_admins"],
"level": "ERROR",
"propagate": True,
}
},
}dedup_window_seconds(int, default180)- Identical error emails during this window are suppressed.
summary_email_every_seconds(int, default60)- If duplicates keep coming, send periodic summary emails.
state_file_path(str | None, defaultNone)- When set, dedup state is shared between worker processes via file lock.
- Use a location writable by all app workers.
- If omitted, dedup works per-process only.
- Single host / shared filesystem: use
state_file_pathfor cross-worker dedup. - Multiple pods/hosts without shared filesystem: dedup is per-instance. A cache backend (Redis etc.) can be added in future versions.
pip install -e .[dev]
pytest -q