Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsDjango 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.
- 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. - 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.
- 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.
- 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.
- 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.
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.
#1 Best Overall
| 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.
Rank #2
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.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →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.
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
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.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →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=DEBUGin 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
- Confirm the logger name. Print
logger.nameor check the module path to see which logger your code actually uses. - Check the logger’s level, then each parent’s level in the dotted-name hierarchy.
- Check the handler’s level and any filter attached to it. A filter that requires
DEBUGto be true produces no output in production. - Look for
propagatesettings that send one record to two handlers, and fordisable_existing_loggersset toTrue, which silently disables existing loggers. - 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.
Quick Recap
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.
Recommended Free Tools




