Skip to Content
Course content

150: Logging vs print for Diagnostics

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

Why can't I just keep using print() for my diagnostics?

Look, when you're hacking together a small script on your local machine, print() is great. It's fast. But the moment your code moves to a server or runs as a background task, print() becomes a liability. Why? Because prints are "dumb." They just dump text to the standard output. If your script crashes at 3:00 AM on a remote server, those print statements are gone unless you've manually redirected the entire shell output to a file, which is messy and imprecise.

I've spent way too many hours of my career hunting through massive text files for a specific "Here" or "Test 1" print statement I forgot to remove. With the logging module, you can categorize your messages. You can tell Python, "Only show me the critical errors right now, but keep the detailed debug info in the background in case I need it later."

How do I actually set this up without it feeling over-engineered?

You don't need a complex configuration file to get started. For most of your projects, logging.basicConfig() is all you need. Let's imagine we're building a price tracker that scrapes a website. Instead of printing "Checking price...", we use the logger.

import logging

# Basic config: set the level and the format
logging.basicConfig(
    level=logging.DEBUG, 
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def check_price(item_id):
    logging.debug(f"Fetching data for item {item_id}...") 
    # Imagine a network request happens here
    success = False 
    if not success:
        logging.error(f"Failed to retrieve price for {item_id}")

check_price("B07X123")

Notice the format argument. Now, every message automatically gets a timestamp and a severity level. You didn't have to manually type datetime.now() inside every single diagnostic message. That's a huge quality-of-life win.

What's the difference between DEBUG, INFO, WARNING, ERROR, and CRITICAL?

This is where people usually get tripped up. It's not a hard rule, but I follow this mental model:

  • DEBUG: "The guts." Information that is only useful when you're actively trying to find a bug. (e.g., "Variable x is currently 42")
  • INFO: "The heartbeat." General confirmation that things are working as expected. (e.g., "Price tracker started successfully")
  • WARNING: "The yellow flag." Something odd happened, but the app is still running. (e.g., "API response took longer than 2 seconds")
  • ERROR: "The red flag." A specific operation failed. (e.g., "Could not connect to the database")
  • CRITICAL: "The panic button." The entire program must stop immediately. (e.g., "Disk full, cannot write logs or data")

The magic happens when you change the level in basicConfig(). If you set it to logging.WARNING, all those DEBUG and INFO messages simply vanish from your output. You don't have to delete your code; you just turn down the volume.

Can I save these logs to a file instead of just watching them scroll by?

Absolutely. And in a real production environment, you almost always should. You just add a filename argument to your config. I usually recommend using a specific filename so you don't accidentally clutter your root directory.

logging.basicConfig(
    filename='app_diagnostics.log', 
    filemode='a', # 'a' for append, 'w' to overwrite every time
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.info("Application started. Logging to file now.")

One tip from the trenches: be careful with filemode='w'. If you use 'w', you'll wipe your logs every time the script restarts. Stick with 'a' (append) unless you have a very specific reason not to. In larger systems, we use "Rotating File Handlers" so the log files don't grow to 10GB and eat the whole hard drive, but for now, a simple file is a massive upgrade over print().




📋 Practical Task

Exercise: Converting the Price Tracker to a Professional Logger

You have been handed a legacy script that uses print() for everything. Your task is to replace the prints with a proper logging system.

Requirements:

  • Import the logging module.
  • Configure the logger to save messages to a file named tracker.log.
  • Set the logging level to DEBUG so all messages are captured.
  • Replace the print("Starting scrape...") with an INFO log.
  • Replace the print(f"Checking item {item}") with a DEBUG log.
  • Replace the print("Error: Website unreachable") with an ERROR log.
# STARTING CODE
def run_tracker(items):
    print("Starting scrape...")
    for item in items:
        print(f"Checking item {item}")
        # Simulate a failure for the last item
        if item == "BrokenLink":
            print("Error: Website unreachable")

run_tracker(["Laptop", "Mouse", "BrokenLink"])
# END STARTING CODE
Rating
0 0

There are no comments for now.

to be the first to leave a comment.