Skip to Content
Course content

151: Configuring the logging Module

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've spent a lot of time reviewing PRs from junior devs, and there is one mistake that appears almost every single time someone starts using the logging module: they treat logging.basicConfig() like a setter method that they can call whenever they want to change how the program logs.

The Myth: Calling basicConfig repeatedly updates your settings

You'll often see code that looks like this. The developer thinks they can switch from a "general" mode to a "debug" mode halfway through the script by calling the configuration function again.

import logging

# Start with a basic setup
logging.basicConfig(level=logging.INFO)
logging.info("System is starting up...") 

# Now we've hit a complex part of the code, let's turn on DEBUG logs!
logging.basicConfig(level=logging.DEBUG) 
logging.debug("Checking the internal state of the data buffer...") 
# Result: The debug message never prints.

Here is the catch: basicConfig does absolutely nothing if the root logger already has handlers configured. Since the first call created a handler (the default stream to stderr), the second call is simply ignored by Python. It doesn't throw an error; it just silently fails to do anything. This leads to hours of frustration where you're staring at your code wondering why your debug logs aren't appearing.

The Reality: Managing the Root Logger and its Handlers

To actually control your logging, you need to understand that logging is a hierarchy. At the top is the "root logger." When you call logging.info(), you're actually calling a convenience method that tells the root logger to handle the message.

If you need to change the log level on the fly, or if you want your logs to go to both a file and the console, you have to talk to the logger object directly or manipulate its handlers. I personally prefer creating a named logger for every module, but for this lesson, let's look at how to properly configure the root logger for a data ingestion script.

import logging

# Set up the root logger once
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG) # Set the lowest level you'll possibly need

# Create a console handler for general info
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# Create a file handler for detailed debugging
file_handler = logging.FileHandler('ingestion_debug.log')
file_handler.setLevel(logging.DEBUG)

# Add them both to the root logger
root_logger.addHandler(console_handler)
root_logger.addHandler(file_handler)

logging.info("Ingestion started.") # Goes to console and file
logging.debug("Connecting to API endpoint 0x445...") # Goes ONLY to file

By separating the Logger (the entry point) from the Handler (the destination), you gain total control. The logger acts as a gatekeeper; if the logger's level is set to INFO, a DEBUG message will be dropped before it even reaches the handlers. That's why I set the root logger to DEBUG in the example above—it opens the gate wide, allowing the individual handlers to decide what they actually want to keep.

One quick tip: if you're building a library that other people will use, never call basicConfig(). If you do, you're forcing your logging preferences on the person using your library, which is a great way to make them hate your package. Instead, just use logger = logging.getLogger(__name__) and let the end-user configure the root logger in their main script.




📋 Practical Task

Exercise: Building a Dual-Destination Audit Logger

You are building a financial transaction processor. For security and auditing reasons, you need to implement a logging system with the following strict requirements:

  • All WARNING and ERROR logs must be written to a file named audit_trail.log.
  • All INFO and higher logs must be printed to the console.
  • DEBUG logs should be completely ignored by both the console and the file to avoid leaking sensitive memory addresses in production.

Your Task: Write a script that configures the root logger to satisfy these requirements. After configuration, trigger one debug(), one info(), and one error() message. Verify that the debug message appears nowhere, the info message appears only in the console, and the error message appears in both.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.