DEV Community

Cover image for Logging in Django: From Basics to Production (Part 2: Python Logging Fundamentals)
rezoMoon
rezoMoon

Posted on

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

๐Ÿ“Œ This article is Part 2 of a multi-part series: *"Logging in Django: From Basics to Production"*.

If you missed Part 1, check it out here: Part 1: Why Logging?

Think of a logging system as a data pipelineโ€”by setting up and configuring that pipeline properly, you can seamlessly manage and direct your application's logs.

Before diving into the code details, let's examine two real-world log outputs to understand exactly what we are aiming for in Python and Django applications.

Real-world Output in a Python Service:

2026-09-03 18:55:01,234 - root - INFO - Application started successfully.
2026-09-03 18:55:02,512 - my_project.utils - DEBUG - Connecting to Redis cache at 127.0.0.1:6379
2026-09-03 18:55:02,589 - my_project.utils - INFO - Cache connection established.
2026-09-03 18:55:10,102 - my_project.api - ERROR - Failed to parse incoming payload.
Traceback (most recent call last):
  File "/home/user/project/data_processor.py", line 42, in parse_payload
    data = json.loads(payload)
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
2026-09-03 18:55:35,401 - root - INFO - Periodic cleanup completed: 14 stale sessions purged.
Enter fullscreen mode Exit fullscreen mode

Standard Output in the Django Framework:

[2026-09-03 18:50:22,104] INFO [django.server:164] "GET /api/v1/products/ HTTP/1.1" 200 4520
[2026-09-03 18:51:05,312] WARNING [django.request:228] Forbidden (Permission Denied): /api/v1/admin/dashboard/
[2026-09-03 18:52:14,891] ERROR [django.request:241] Internal Server Error: /api/v1/checkout/
Enter fullscreen mode Exit fullscreen mode

What Information Do These Lines Give Us?

  • Timestamp: Exactly down to the millisecond when the event occurred.

  • Log Level: Is this a routine notification, a warning, or a critical failure requiring immediate intervention?

  • Origin (Logger Name / File / Line): Exactly which file, function, or module dispatched the event.

  • Event Description & Traceback: What happened, and what was the state of the execution context?

These are the core elements you must consider when designing and configuring your logging setup. But how do we produce such structured logs, and what options, controls, and configurations are available under the hood?

Letโ€™s start with the simplest possible approach.

Your First Log in Python and the Concept of "Root"

Pythonโ€™s built-in logging system uses a modular, object-oriented pipeline composed of several components.

To record logs, we import the standard logging module:

# level/basic.py
import logging

logging.basicConfig(level=logging.INFO)
logging.info("Your First Log in Python!")
Enter fullscreen mode Exit fullscreen mode

Running this code produces the following console output:

INFO:root:Your First Log in Python!
Enter fullscreen mode Exit fullscreen mode

Why was INFO:root: prefixed to the message?

  • Log Level (INFO): Defines the severity and nature of the event.

  • Logger Name (root): Loggers in Python follow a hierarchical tree structure. When you directly invoke helper functions like logging.info(), the event is passed to the top node of this tree: the Root Logger.

Understanding Log Levels and Their Numeric Values

In Python, you can categorize events into distinct severity levels and handle each level differently depending on your system requirements.

There are 5 standard log levels:

Level Filtering: Why Did We Specify level=logging.INFO?

The default threshold for the Python logger is WARNING (numeric value 30). The logging engine only processes and outputs messages whose numeric severity is greater than or equal to the configured threshold.

Consider this example:

import logging

logging.basicConfig(level=logging.ERROR)

logging.info("Info log - ignored")
logging.warning("Warning log - ignored")
logging.error("Error log - will be displayed")
Enter fullscreen mode Exit fullscreen mode

Only the last line is printed. Because ERROR (40) satisfies the threshold, any level lower than 40 is suppressed.
Configuration Strategies

While basicConfig works for simple single-file scripts, it is not sufficient for production systems.

  1. Quick Setup via basicConfig (Scripts & Testing)
import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(name)s - %(filename)s:%(lineno)d - %(message)s",
    filemode="a",
    filename="app.log"
)

Enter fullscreen mode Exit fullscreen mode
  1. The Standard Enterprise Approach: Module Namespaces (__name__)

In modular applications, you should never log everything directly to the root logger. Instead, each module should declare its own independent logger tied to Python's module namespace using __name__:

# users/services.py
import logging

logger = logging.getLogger(__name__)

def process_user_profile(username: str):
    logger.debug("Processing profile for user: %s", username)

    try:
        # Business logic fetching user profile
        logger.info("Successfully fetched profile for user: %s", username)
    except Exception:
        # logger.exception automatically attaches the traceback
        logger.exception("Failed to process profile for user: %s", username)
Enter fullscreen mode Exit fullscreen mode

_
Why is this pattern critical?_

When called inside users/services.py, __name__ automatically resolves to the logger name users.services.

In larger systems, this hierarchy allows granular control. For example, you can selectively set loggers under billing.* to output at DEBUG level while keeping third-party libraries or stable core modules at INFO or WARNING.
What to expect in this series:

  • Part 1: Why Logging? (The philosophy, MTTR, and real-world scenarios)

  • Part 2:** Python Logging Fundamentals**: Loggers, Handlers, Formatters & Structured Logging (Current)

Part 3: *Mastering Django LOGGING Configuration in Production *(Log rotation, correlation IDs, and security considerations)

๐Ÿ’ฌ Discussion:

Do you configure distinct log levels per module in your projects, or do you still rely on global logger settings? Let's discuss in the comments below!

Top comments (0)