Catch Django exceptions and send formatted error reports to Telegram with optional database logging
Project description
django-telegram-notifier
Catch unhandled Django exceptions and send formatted error reports to Telegram — like a lightweight Sentry. Includes full traceback as a .py file attachment with syntax highlighting, smart exception classification, optional database logging with a rich admin dashboard, and async support.
Features
- Automatic exception catching via middleware (sync & async)
- Non-blocking — Telegram API calls run in a background thread, never slowing down your response
- Smart exception classification — auto-assigns level & severity based on exception type
- Formatted Telegram messages with emoji levels, request context, and traceback preview
- Full traceback as
.pyfile — Telegram renders Python syntax highlighting - Exception filtering — ignore specific exceptions, paths, or use custom filter functions
- Optional database logging — store every exception with request details, severity, status
- Rich admin dashboard — stats, charts, action toolbar, prev/next navigation, Monaco editors
- Light & dark mode admin — auto-detects system/Django admin theme
- Configurable logger — use stdlib logging, loguru, structlog, or any custom logger
- Celery support — optionally offload reporting to a Celery task
- Decorator for wrapping individual functions/views
- Multi-chat support — send to multiple Telegram chats
- Proxy support for restricted environments
- Management command to clean up old exception logs
Quick Start
1. Install
pip install django-telegram-notifier
2. Get a Telegram Bot Token
- Open Telegram, search for @BotFather
- Send
/newbotand follow the prompts - Copy the bot token (e.g.
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11) - Send a message to your bot, then visit
https://api.telegram.org/bot<TOKEN>/getUpdatesto find yourchat_id
3. Configure
Add to your Django settings.py:
INSTALLED_APPS = [
# ...
"telegram_notifier",
]
MIDDLEWARE = [
# ... other middleware ...
"telegram_notifier.middleware.GlobalExceptionReporterMiddleware",
]
TELEGRAM_NOTIFIER = {
"BOT_TOKEN": "your-bot-token",
"CHAT_IDS": ["your-chat-id"],
}
4. Migrate (only if using database logging)
python manage.py migrate
That's it. Any unhandled exception will now send a notification to your Telegram chat.
Settings
All settings go inside the TELEGRAM_NOTIFIER dictionary in your Django settings:
TELEGRAM_NOTIFIER = {
# Required
"BOT_TOKEN": "your-bot-token",
"CHAT_IDS": ["chat-id-1", "chat-id-2"],
# Optional (defaults shown)
"ENVIRONMENT": None, # e.g. "production", "staging" — shown in messages
"PROXY": None, # e.g. "socks5://127.0.0.1:1080"
"MESSAGE_MAX_LENGTH": 4000, # max message length (Telegram limit)
"STORE_EXCEPTIONS": False, # save exceptions to database
"CLEANUP_DAYS": 30, # days to keep exception logs
# Filtering
"IGNORE_EXCEPTIONS": [], # exception class names to skip
"IGNORE_PATHS": [], # URL path prefixes to skip
"FILTER": None, # custom callable(exc, request) -> bool
# Logging
"LOGGER": None, # custom logger instance (loguru, structlog, etc.)
# Async / Celery
"CELERY_TASK": None, # Celery task for offloading (see Celery section)
}
| Setting | Type | Default | Description |
|---|---|---|---|
BOT_TOKEN |
str |
required | Telegram Bot API token |
CHAT_IDS |
list[str] |
required | Telegram chat IDs to send notifications to |
ENVIRONMENT |
str | None |
None |
Environment name shown in messages (e.g. "production") |
PROXY |
str | None |
None |
Proxy URL for Telegram API requests |
MESSAGE_MAX_LENGTH |
int |
4000 |
Maximum message length before truncation |
STORE_EXCEPTIONS |
bool |
False |
Save exceptions to ExceptionLog model |
CLEANUP_DAYS |
int |
30 |
Days to keep exception logs (used by cleanup command) |
IGNORE_EXCEPTIONS |
list[str] |
[] |
Exception class names to skip (checks MRO) |
IGNORE_PATHS |
list[str] |
[] |
URL path prefixes to skip |
FILTER |
callable | None |
None |
Custom filter function (exc, request) -> bool |
LOGGER |
object | None |
None |
Custom logger instance (any object with .info(), .error()) |
CELERY_TASK |
Task | None |
None |
Celery task to offload reporting (see below) |
Exception Filtering
Control which exceptions get reported with three layers of filtering:
Ignore by exception class
TELEGRAM_NOTIFIER = {
# ...
"IGNORE_EXCEPTIONS": [
"Http404",
"PermissionDenied",
"DisallowedHost",
"Throttled",
],
}
This checks the exception's MRO, so "DatabaseError" also catches IntegrityError, OperationalError, etc.
Ignore by path
TELEGRAM_NOTIFIER = {
# ...
"IGNORE_PATHS": [
"/health",
"/favicon.ico",
"/.well-known",
],
}
Custom filter function
TELEGRAM_NOTIFIER = {
# ...
"FILTER": lambda exc, request: (
# Only report in production
not getattr(request, 'path', '').startswith('/admin/')
),
}
Return True to report, False to skip. Filters are evaluated in order: IGNORE_EXCEPTIONS -> IGNORE_PATHS -> FILTER.
Smart Exception Classification
Exceptions are automatically classified by level and severity based on their type:
| Category | Level | Severity | Examples |
|---|---|---|---|
| System failures | critical |
critical |
MemoryError, RecursionError, SystemExit |
| Infrastructure | error |
high |
DatabaseError, ConnectionError, TimeoutError |
| Client/validation | warning |
low |
Http404, ValidationError, PermissionDenied, Throttled |
| Default (bugs) | error |
moderate |
TypeError, KeyError, AttributeError |
You can override auto-classification by passing explicit level and severity to report_exception().
Usage
Middleware (recommended)
The middleware catches all unhandled exceptions automatically. Place it after Django's built-in middleware:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"telegram_notifier.middleware.GlobalExceptionReporterMiddleware",
]
The middleware is async-capable — works with both WSGI and ASGI servers.
Decorator
Wrap individual functions or views:
from telegram_notifier import telegram_exception_notifier
@telegram_exception_notifier
def my_task():
# if this raises, it gets reported to Telegram
do_something_risky()
# Also works with async functions
@telegram_exception_notifier
async def my_async_task():
await do_something_risky()
Manual Reporting
Report exceptions programmatically:
from telegram_notifier import report_exception
try:
do_something()
except Exception as exc:
report_exception(exc, level="critical", severity="high")
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
exc |
Exception |
required | The exception to report |
request |
HttpRequest | None |
None |
Django request (adds path, method, body, user) |
body |
bytes | None |
None |
Request body |
level |
str | None |
auto | Overrides auto-classification |
severity |
str | None |
auto | Overrides auto-classification |
Custom Logger
By default, the package uses Python's stdlib logging. You can plug in any logger:
# loguru
from loguru import logger
TELEGRAM_NOTIFIER = { ..., "LOGGER": logger }
# structlog
import structlog
TELEGRAM_NOTIFIER = { ..., "LOGGER": structlog.get_logger() }
# silent (disable all logging)
TELEGRAM_NOTIFIER = { ..., "LOGGER": None }
Any object with .info() and .error() methods works.
Non-Blocking & Celery
By default, all Telegram API calls and database writes run in a background daemon thread — your request response is never delayed by slow network or Telegram downtime.
For projects using Celery, you can offload reporting to a Celery task instead:
# tasks.py
from celery import shared_task
from telegram_notifier.report import _do_report
@shared_task
def send_telegram_report(
exc_class_name, exc_message, message,
traceback_content, level, severity, request_data,
):
_do_report(
exc_class_name=exc_class_name,
exc_message=exc_message,
message=message,
traceback_content=traceback_content,
effective_level=level,
effective_severity=severity,
request_data=request_data,
body=None,
)
# settings.py
from myapp.tasks import send_telegram_report
TELEGRAM_NOTIFIER = {
# ...
"CELERY_TASK": send_telegram_report,
}
When CELERY_TASK is set, reporting is dispatched via .delay() instead of a thread — giving you retries, monitoring, and rate limiting through your existing Celery infrastructure.
Telegram Message Format
Each notification is sent as a document (.py file with full traceback) with a caption containing:
🔴 ValueError in production
Message: invalid literal for int()
Timestamp: 2026-03-11 12:30:45
▸ Traceback (preview)
value = int(user_input)
ValueError: invalid literal for int() with base 10: 'abc'
▸ Request
Path: /api/users/
Method: POST
Body: {"name": "test"}
User: admin@example.com
Level emojis: ⚪ debug · 🔵 info · 🟡 warning · 🔴 error · ⛔ critical
The attached .py file contains the complete traceback with Python syntax highlighting in Telegram.
Database Logging & Admin Dashboard
Enable with "STORE_EXCEPTIONS": True. This creates an ExceptionLog entry for every reported exception.
Admin List View
- Summary stats — exceptions in last 24h/7d, by level, unresolved count
- Charts — exceptions timeline (24h bar chart) and level breakdown (doughnut)
- Colored badges — level, severity, method, environment, status
- Inline status editing — change status directly from the list
- Bulk actions — Mark as Resolved, Mark as Ignored, Mark as Seen, Resend to Telegram
- Date hierarchy — quick date navigation
- Filters — by level, severity, status, sent, environment, method, date
Admin Detail View
- Action toolbar — Resolve, Seen, Ignore, Resend to Telegram (AJAX, no reload)
- Occurrence stats — how many times this exception occurred in 24h/7d/total
- Prev/Next navigation — browse exceptions chronologically
- Monaco editors — syntax-highlighted traceback (Python), headers/body/params (JSON)
- Copy/Format/Minify buttons — on all JSON sections with tooltip feedback
- Collapsible headers section
- Light & dark mode — auto-detects Django admin theme and system preference
ExceptionLog Fields
| Field | Type | Description |
|---|---|---|
exception_class |
CharField |
e.g. "ValueError" |
message |
TextField |
Exception message |
traceback |
TextField |
Full traceback |
level |
CharField |
debug, info, warning, error, critical |
severity |
CharField |
low, moderate, high, critical |
status |
CharField |
new, seen, resolved, ignored |
path |
CharField |
Request path |
method |
CharField |
HTTP method |
query_params |
JSONField |
Query string parameters |
body |
TextField |
Request body |
user_info |
CharField |
Authenticated user string |
ip_address |
GenericIPAddressField |
Client IP (with X-Forwarded-For support) |
headers |
JSONField |
Request headers (sensitive headers filtered) |
view_name |
CharField |
Django view name |
hostname |
CharField |
Server hostname |
environment |
CharField |
Environment from settings |
is_sent |
BooleanField |
Whether Telegram notification was sent |
created_at |
DateTimeField |
When the exception occurred |
Querying Exceptions
from telegram_notifier import ExceptionLog, Status, Level
# Unresolved errors
ExceptionLog.objects.filter(status=Status.NEW, level=Level.ERROR)
# Errors in the last 24 hours
from django.utils.timezone import now
from datetime import timedelta
ExceptionLog.objects.filter(created_at__gte=now() - timedelta(hours=24))
# Mark as resolved
ExceptionLog.objects.filter(pk=1).update(status=Status.RESOLVED)
Cleanup Command
Delete old exception logs:
# Use CLEANUP_DAYS setting (default: 30)
python manage.py cleanup_exceptions
# Override with custom days
python manage.py cleanup_exceptions --days=7
Schedule with cron for automatic cleanup:
0 3 * * * python /path/to/manage.py cleanup_exceptions
Public API
from telegram_notifier import (
# Core
report_exception, # Report an exception manually
notify_error_via_telegram, # Send raw message to Telegram
classify_exception, # Get (level, severity) for an exception
# Message building
build_exception_message, # Build formatted HTML message
build_traceback_content, # Get full traceback string
# Middleware & decorator
GlobalExceptionReporterMiddleware,
telegram_exception_notifier,
# Models & choices
ExceptionLog, # Database model (lazy-loaded)
Level, # debug, info, warning, error, critical
Severity, # low, moderate, high, critical
Status, # new, seen, resolved, ignored
)
Requirements
- Python >= 3.11
- Django >= 5.2
- httpx >= 0.28.1
Development
git clone https://github.com/ganiyevuz/django-telegram-notifier.git
cd django-telegram-notifier
# Install dependencies
uv sync --group dev
# Copy env file and add your bot token
cp .env.example .env
# Run tests
pytest
# Run linter
ruff check .
License
MIT
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 django_telegram_notifier-0.2.0.tar.gz.
File metadata
- Download URL: django_telegram_notifier-0.2.0.tar.gz
- Upload date:
- Size: 26.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b4203f41c46084de8efe8cd8f593aeb7108507fd6517726c9151beff83cdd04
|
|
| MD5 |
0b132124bc1e71d427a71f17a06a9d69
|
|
| BLAKE2b-256 |
ae66bd276bffb1a5c457ceadbc31ec71ea2ee6356cbb20699bc225b7bda396fb
|
File details
Details for the file django_telegram_notifier-0.2.0-py3-none-any.whl.
File metadata
- Download URL: django_telegram_notifier-0.2.0-py3-none-any.whl
- Upload date:
- Size: 32.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad00ee0beebb0e0fe5facee543b795f657bf3c7b911e134e2a4a9b18f60207b2
|
|
| MD5 |
fc17ae6d3556d5bde0488d7fe438dd97
|
|
| BLAKE2b-256 |
6759d953b1175da008a8cb51d4a00c302496738810353c62e825235ec1d68c4b
|