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
Metadata
Release files for django-telegram-notifier 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_telegram_notifier-0.2.1.tar.gz | 26.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_telegram_notifier-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.1 kB
Release files / django_telegram_notifier-0.2.1.tar.gz
| Download URL | django_telegram_notifier-0.2.1.tar.gz |
|---|---|
| Size | 26.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4a2fc4eb4a946db4ef492639ab5d6a19a3f43d643032041e249c0d5371c1bf2c
|
|
BLAKE2b-256 checksum How to use checksums |
09c028ec35709bae1293f1947fe8b7303aa05939539b137d490c78d3616894cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.19
|
Release files / django_telegram_notifier-0.2.1-py3-none-any.whl
| Download URL | django_telegram_notifier-0.2.1-py3-none-any.whl |
|---|---|
| Size | 32.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c01106de75935fbcc335f9a10f124ee834f38d800168a8d4200823bf7ad3877b
|
|
BLAKE2b-256 checksum How to use checksums |
66a18742cc53bea42a233556737afae18e3a992e89e731fba2cf2602e1977861
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.19
|