Skip to main content

django-discordo

A janky Discord webhook handler originally written for OTIS-WEB.

Features, apparently

  • 🎨 Color-coded log levels with emoji indicators
  • 📝 Automatic formatting of Django request data
  • 🔒 Automatic redaction of sensitive data (passwords, tokens)
  • 🎯 Support for custom log levels (VERBOSE, SUCCESS, ACTION)
  • 🔧 Configurable webhook URLs per log level
  • 📊 Rich metadata including user info, status codes, and stack traces

Installation

For example, if using uv:

uv add django-discordo

Contrary to the name, Django is actually an optional dependency. This can also be used for logging in non-Django applications; in that case Django-specific functionality is turned off.

Configuration

1. Create a Discord Webhook

  1. Go to Server Settings → Integrations → Webhooks
  2. Click "New Webhook"
  3. Copy the webhook URL

2. Configure Webhook URL

There are two possible ways to do this (first one takes precedence):

Use settings.py (for Django applications)

Add your webhook URL to your Django settings.py:

# Simple configuration - single webhook for all log levels
DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/YOUR_WEBHOOK_URL"

# OR: Advanced configuration - different webhooks per log level
DISCORD_WEBHOOK_URLS = {
    "CRITICAL": "https://discord.com/api/webhooks/CRITICAL_WEBHOOK",
    "ERROR": "https://discord.com/api/webhooks/ERROR_WEBHOOK",
    "WARNING": "https://discord.com/api/webhooks/WARNING_WEBHOOK",
    "DEFAULT": "https://discord.com/api/webhooks/DEFAULT_WEBHOOK",  # Fallback for other levels
}

(Webhook URL's are considered secrets, so you probably don't want to actually hardcode the URL's into settings.py if your source code is public.)

Using environment variables directly

If the URLs are not configured in settings.py (or Django is not present at all) then django-discordo will check environment variables instead:

# In your .env file
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR_WEBHOOK_URL
# Or level-specific
DISCORD_WEBHOOK_URL_ERROR=https://discord.com/api/webhooks/YOUR_ERROR_WEBHOOK_URL

3. Using the handler

In Django, add the Discord handler to your Django settings.py, e.g.

import logging

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "discord": {
            "class": "django_discordo.DiscordWebhookHandler",
            "level": "WARNING",
        },
    },
    "root": {
        "handlers": ["discord"],
        "level": "INFO",
    },
}

Otherwise, import the webhook handler directly, say:

from django_discordo import DiscordWebhookHandler

logger = logging.getLogger("root")
logger.setLevel(logging.INFO)
logger.addHandler(DiscordWebhookHandler())
logger.addHandler(logging.StreamHandler())

Delivery is non-blocking

Since v1.2, the HTTP request to Discord happens on a background worker thread, so e.g. a Discord outage doesn't cause everything to burn.

You can tune this with handler options:

LOGGING = {
    "handlers": {
        "discord": {
            "class": "django_discordo.DiscordWebhookHandler",
            "level": "WARNING",
            # all of the below are optional; defaults shown
            "blocking": False,  # True = post inline, the pre-1.2 behavior
            "timeout": (3.05, 10),  # (connect, read) seconds, or a single float
            "queue_size": 1000,  # max records waiting to be sent
            "shutdown_timeout": 5.0,  # seconds to wait for delivery at exit
        },
    },
}

Custom Log Levels

django-discordo provides three custom log levels in addition to Django's standard levels:

  • VERBOSE (level 15): Between DEBUG and INFO, for detailed but not critical information
  • SUCCESS (level 25): Between INFO and WARNING, for successful operations
  • ACTION (level 35): Between WARNING and ERROR, for important user actions

Usage Example

import logging
from django_discordo import VERBOSE_LOG_LEVEL, SUCCESS_LOG_LEVEL, ACTION_LOG_LEVEL

logger = logging.getLogger(__name__)

# Using custom log levels
logger.log(VERBOSE_LOG_LEVEL, "Student has submitted the problem set.")
logger.log(SUCCESS_LOG_LEVEL, "Student has found new diamond.")
logger.log(ACTION_LOG_LEVEL, "Student has updated a link in ARCH, please check it.")

Advanced Configuration

Filtering Logs

You can add filters to prevent certain logs from being sent to Discord; e.g. OTIS-WEB filters out certain 404 errors

def filter_useless_404(record):
    if record.args and len(record.args) >= 2:
        return "wp-include" not in str(record.args[1])
    return True


LOGGING = {
    "filters": {
        "filter_useless_404": {
            "()": "django.utils.log.CallbackFilter",
            "callback": filter_useless_404,
        },
    },
    "handlers": {
        "discord": {
            "class": "django_discordo.DiscordWebhookHandler",
            "level": "WARNING",
            "filters": ["filter_useless_404"],
        },
    },
}

Testing Mode

When running tests, you may want to disable Discord logging to avoid spam:

import logging
from django_discordo import ACTION_LOG_LEVEL

if TESTING:
    logging.disable(ACTION_LOG_LEVEL)

This disables all logs at ACTION level and below (including SUCCESS, INFO, VERBOSE, and DEBUG).

How It Works

When a log record is emitted:

  1. The handler formats the log message with appropriate emoji and color
  2. Extracts metadata (user, module, filename, line number, status code)
  3. Includes Django request details (method, path, user agent, POST data)
  4. Redacts sensitive fields (passwords, tokens)
  5. Hands the finished embed to a background thread, which posts it to the webhook

Steps 1–4 run on the thread that logged, because the request object is only valid there; only the network call in step 5 is deferred.

Discord Embed Format

Each log message appears as a Discord embed with:

  • Title: Log message (with emoji indicator)
  • Color: Coded by log level (red for errors, yellow for warnings, etc.)
  • Fields: Status code, log level, module, user, filename
  • Description: Detailed message, exception traceback, and request data

Metadata

Release files for django-discordo 1.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-discordo 1.2.1
File Size Uploaded
django_discordo-1.2.1.tar.gz 8.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-discordo 1.2.1
File Interpreter ABI Platform
django_discordo-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 19.0 kB

Release files / django_discordo-1.2.1.tar.gz

Download URL django_discordo-1.2.1.tar.gz
Size 8.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d4453be56d2010869668dc60ae1c32eb389e41e0904c26fcb3600360a4b0f542
BLAKE2b-256 checksum
How to use checksums
45dca1b7883b7f723deaea603381d24cc46f633991497d2c3d48e24ce9ac1f06
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / django_discordo-1.2.1-py3-none-any.whl

Download URL django_discordo-1.2.1-py3-none-any.whl
Size 10.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
36f9a77ac2e6d2990cf3bad572ff6c4d0bb19bbd0e8f5b7c998bf650bbd0c7bf
BLAKE2b-256 checksum
How to use checksums
a9598029ced749a59ef74ee19cd18ec45dad57903727d4793cac28a92040b53b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.2.1 This release

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page