About Loggers and Django Logging Configuration

Always create loggers this way at the top of a module:

import logging
logger = logging.getLogger(__name__)
...
logger.critical("Hacking attempt detected.")

Here __name__ resolves to:

  • "__main__" if the module is executed directly
  • the module's full dotted path, like "myproject.apps.myapp.views", otherwise.

The module's full dotted path maps directly onto Python's hierarchical logging system. A log message from "myproject.apps.myapp.views" automatically propagates up through "myproject.apps.myapp""myproject.apps""myproject" → root.

This means your Django LOGGING config can target entire subtrees of your project with a single entry:

# settings.py

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {"class": "logging.StreamHandler"},
    },
    "loggers": {
        # Catches myapp, myapp.views, myapp.utils, etc.
        "myproject.apps.myapp": {
            "handlers": ["console"],
            "level": "DEBUG",
            "propagate": False,
        },
        # More specific — overrides the parent for this subtree only
        "myproject.apps.myapp.utils": {
            "handlers": ["console"],
            "level": "WARNING",
        },
        # Django's own internal logger
        "django": {
            "handlers": ["console"],
            "level": "INFO",
        },
    },
}

A few things to keep in mind:

  • disable_existing_loggers: False keeps loggers that were created at import time alive (while set to True by default, all loggers from the default configuration will be disabled).
  • More specific loggers win over parent ones, so myproject.apps.myapp.utils at WARNING takes precedence over myproject.apps.myapp at DEBUG for that subtree.
  • propagate: False prevents the same message being logged twice by both myproject.apps.myapp and the root logger.

Tips and Tricks Programming Logging Django 6.x Django 5.2 Django 4.2 Python 3