Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk7 min

Logging in Django: From Basics to Production (Part 2: Python Logging Fundamentals)

Django logging runs on Python's logging module, configured through the LOGGING dictionary. Learn how records flow through loggers, propagation and handlers, plus the production risks to review.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Django logging is Python’s standard logging module, configured through a single LOGGING dictionary in your settings. To configure it reliably, you need to understand one pipeline: application code creates a record, the logger compares that record’s level and applies propagation, and handlers decide where eligible records are written. This part covers that model first, then shows how Django’s settings map onto it, and finishes with the production risks you should check before deploying.

How a log record moves through the system

Every log call follows the same path. Understanding the order makes most logging surprises easy to diagnose.

As an Amazon Associate I earn from qualifying purchases.

  1. Your code creates a record. A call such as logger.warning("...") produces a record with a level, a message, the logger’s name, and optional metadata such as traceback information.
  2. The logger checks its level. If the record’s level is below the logger’s configured level, it is dropped here and never reaches a handler.
  3. Propagation moves the record upward. By default, a record continues to parent loggers, up to the root logger, unless a logger has propagation turned off.
  4. Each handler checks its own level and filters. A record must pass the handler’s level and any attached filters before the handler acts on it.
  5. The handler’s formatter renders the record into text or another output form, and the handler writes it to its destination, such as a stream, a file, or an email.

Because a record can pass several checkpoints, a message can disappear at the logger, at the handler, or at a filter. Each of those is a separate control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The four configuration roles

Django’s logging configuration is built from four kinds of objects, and each has a distinct job. Django’s logging overview describes them as complementary, not interchangeable.

Component Job Typical example
Logger Names the source of records and sets a level. This is the entry point your code uses. logging.getLogger(__name__) in a module named myapp.views
Handler Decides what happens to records it receives, and has its own level. A stream handler that writes to standard error
Filter Decides whether a record proceeds, and can modify it. A filter that passes records only when DEBUG is true
Formatter Turns a record into the final text or structure. A format string that includes the level name, logger name, and message

Log levels and what they mean

Django’s documentation describes the five standard levels as severity categories. The numeric values are Python’s standard values, which determine the ordering when a logger or handler filters records.

Level Numeric value Meaning, per Django’s documentation
DEBUG 10 Low-level diagnostic information
INFO 20 General information about the system
WARNING 30 A minor problem
ERROR 40 A major problem
CRITICAL 50 A critical problem

A logger set to WARNING accepts WARNING, ERROR, and CRITICAL records and silently drops DEBUG and INFO records. Choose the level that matches the event’s actual severity. Using ERROR for expected conditions, such as a user entering a wrong password, fills error channels with noise and makes real failures harder to find.

Using loggers in application code

Create one logger per module, named with __name__. The name follows the module path, which lets you configure a whole package with one entry in LOGGING.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import logging

logger = logging.getLogger(__name__)

def charge_card(order_id):
    logger.info("Starting payment for order %s", order_id)
    logger.warning("Gateway responded slowly for order %s", order_id)

Pass values as arguments rather than building the message with an f-string. The logging module formats the message only when a record is actually emitted, so filtered-out messages cost less.

How Django loads the LOGGING setting

Django’s LOGGING setting is a dictionary. Django passes it to the callable named by LOGGING_CONFIG, which defaults to logging.config.dictConfig. This is Python’s standard dictionary-based configuration format, documented in the Django 6.1 settings reference. Django performs this configuration as part of its setup process, so loggers are ready once Django has been set up, which happens automatically in requests, management commands, and the test runner.

Django merges your LOGGING dictionary with its own defaults rather than replacing them entirely. Setting LOGGING_CONFIG to None turns off Django’s automatic configuration step. It does not stop your code from calling logging methods; you then have to configure logging yourself.

A minimal working configuration

Start with a console handler on the root logger. This setup sends WARNING and higher records from any logger to standard error.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "simple": {
            "format": "{levelname} {name} {message}",
            "style": "{",
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
    },
    "root": {
        "handlers": ["console"],
        "level": "WARNING",
    },
}

Keep disable_existing_loggers set to False. When it is True, Django’s own loggers that already exist are disabled. They remain in place but silently discard records and do not propagate them, so a configuration that looks correct can hide Django’s messages entirely. Django’s own examples use False when extending the configuration.

To log your application at a finer level, add a named logger. Setting propagate to False keeps your records from also reaching the root handler.

"loggers": {
    "myapp": {
        "handlers": ["console"],
        "level": "INFO",
        "propagate": False,
    },
},

Add file handlers, filters, or email handlers only when a concrete need exists. For a file handler, the path must be writable by the user that runs the application process; a permission error at startup usually means the path is wrong for that user, not that logging is broken.

Propagation and duplicate output

Propagation is the most common source of both missing and doubled log lines. When a logger propagates, each parent in the dotted-name hierarchy receives the record and can emit it through its own handlers. So if myapp has a console handler and also propagates to a root logger that has a console handler, every myapp record prints twice.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The fix is to decide, for each level of the hierarchy, where records should end up. Either attach the handler in one place only, or set propagate to False on the logger that owns the handler. Check the hierarchy whenever output is missing: a record that never reaches a handler was dropped by a logger level, a handler level, or a filter, in that order.

Best Value
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What Django’s default logging does

Django ships with a default configuration that depends on the DEBUG setting. The Django development logging reference, checked in October 2026, describes the following behavior. These conditions can change between releases, so confirm them against the documentation for the Django version you run.

Logger When DEBUG is True When DEBUG is False
django hierarchy, except django.server INFO and higher go to the console ERROR and higher go to AdminEmailHandler
django.server INFO and higher go to the console INFO and higher go to the console

AdminEmailHandler sends error emails to the addresses listed in the ADMINS setting. Django’s logging reference also notes that, in Django 6.1, the handler’s email_backend argument is deprecated in favor of using. If you pass that argument in a custom handler, update it before upgrading.

Production logging: choosing a destination

The default email path is a notification mechanism, not a log store. Django’s documentation cautions about the security implications of email, because error emails can include request details and tracebacks. Anyone with access to the mailbox then has access to that data, and email is not searchable or retained the way a central log system is.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Compare destinations on the axes that matter operationally:

Destination Where records go Searchable and retained centrally Access control Setup and upkeep Exposure risk
Console or standard streams The process’s stdout or stderr, usually collected by the hosting platform Depends on the platform’s log collector; not stated by Django Governed by who can read the platform’s logs Minimal in the application Lower than email, but request data still appears if you log it
Local file A file on the server Not centralized; you must ship or rotate files yourself File-system permissions Moderate: paths, permissions, rotation Moderate; depends on file permissions and retention
Email (AdminEmailHandler) Addresses in ADMINS No Mailbox access Low, but depends on mail configuration Higher: request details and tracebacks are copied into email
Hosted log-management or application-observability service A third-party service that receives records you send Typically yes; confirm features with the provider Set by the provider’s account and role model Moderate to high; requires integration and credentials Depends on the provider’s terms and what you send; review before adopting

Django’s overview mentions third-party services as one way to get detailed logs with access management. It does not endorse a specific provider, and the choice should follow your team’s retention, access, and compliance requirements.

Risks to review before enabling verbose logging

  • DEBUG volume. The development logging overview notes that setting DJANGO_LOG_LEVEL=DEBUG in its example configuration can expose verbose Django debug logging, including every database query. Enable this for controlled debugging, not as a permanent production setting.
  • Sensitive payloads. Records that include request bodies, headers, or query parameters can carry credentials and personal data. Log identifiers instead of full payloads.
  • Email with tracebacks. Error emails can contain request details. Limit the recipient list and review who can read the mailbox.

Troubleshooting missing or duplicated output

  1. Confirm the logger name. Print logger.name or check the module path to see which logger your code actually uses.
  2. Check the logger’s level, then each parent’s level in the dotted-name hierarchy.
  3. Check the handler’s level and any filter attached to it. A filter that requires DEBUG to be true produces no output in production.
  4. Look for propagate settings that send one record to two handlers, and for disable_existing_loggers set to True, which silently disables existing loggers.
  5. Confirm the file handler’s path exists and is writable by the process user.

Once the pipeline is clear, adding a new logger, handler, or destination becomes a matter of deciding which checkpoint it belongs at.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.