When you start learning programming, the print function is often the first tool used to display output. However, as you transition to writing production-grade code, you’ll encounter the need for a more robust and flexible solution: logging. I discovered this firsthand when a pull request review highlighted the importance of using a logger instead of print. Here’s what I learned about the differences between these tools and how to use them effectively.

An example of the difference

print(f"Processing file: {file_name}")
print(f"Error encountered: {error}")
 
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(processName)s[%(process)d] %(levelname)s %(name)s %(threadName)s: %(message)s',
    handlers=[
        logging.FileHandler("app.log"),
        logging.StreamHandler()
    ]
)
 
logger = logging.getLogger(__name__)
logger.info("Processing file: data.csv")
logger.error("Error encountered: File not found")

With the print you won’t have any difference between the Processing and the Error message while it’ll appear on the logger output

Processing file: data.csv
Error encountered: File not found
 
2025-01-10 14:30:45,123 MainProcess[12345] INFO myapp.processor MainThread: Processing file: data.csv
2025-01-10 14:30:45,124 MainProcess[12345] ERROR myapp.processor MainThread: Error encountered: File not found

When to Use print

The print function is a straightforward way to display output to the console. It’s ideal for simple scripts or situations where minimal setup is needed.

Appropriate Use Cases

  1. Quick Debugging: When you’re testing small code snippets or debugging simple issues, print statements can help you quickly inspect variable values or the flow of execution.
  2. Educational or Experimental Code: In tutorials, learning materials, or interactive sessions, print is often used for its simplicity.
  3. User-Focused Output: If the output is intended directly for the user, such as in command-line interfaces or small scripts, print is appropriate (for example in our Hello, world! context)

Limitations of print

  • Lack of Control: You cannot easily suppress or redirect print output in a large application.
  • No Severity Levels: It doesn’t distinguish between different types of messages, such as errors or warnings.
  • Not Scalable: For large or production-grade applications, print statements quickly become unmanageable.

When to Use logger

The logging module in Python is designed for applications requiring robust and configurable message logging. It is highly flexible and can scale from small scripts to enterprise-level systems.

Appropriate Use Cases

  1. Production Applications: Loggers provide detailed, timestamped messages that are crucial for monitoring and debugging in production environments.
  2. Granular Message Control: Loggers support different levels of severity (DEBUG, INFO, WARNING, ERROR, CRITICAL), allowing you to filter and prioritize messages.
  3. Output Customization: Loggers can output to various destinations (console, files, external systems) and can be formatted with timestamps, log levels, and other metadata.
  4. Concurrent Applications: In multi-threaded or distributed applications, the logging module ensures thread-safe logging and can help trace complex workflows.

Advantages of logger

  • Configurable: Loggers can be configured globally or locally with different handlers and formatters.
  • Non-Intrusive: Logging can be enabled or disabled at runtime without altering the code.
  • Scalable: Suitable for both small scripts and large, distributed systems.

Example: Setting Up a Logger

import logging
 
# Configure the logger
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("app.log"),
        logging.StreamHandler()
    ]
)
 
logger = logging.getLogger(__name__)
 
# Using the logger
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning")
logger.error("This is an error")
logger.critical("This is critical")

Key Differences

Featureprintlogger
Severity LevelsNoneDEBUG, INFO, WARNING, ERROR, CRITICAL
ConfigurationMinimalHandlers, formatters, and runtime control
ScalabilityLimitedScales Well
Output DestinationsConsole onlyConsole, files, external systems
Thread SafetyNoYes

Best Practices

  • Use print for quick debugging or simple scripts.
  • Switch to logger for production code, especially if the application needs robust monitoring or error tracking.
  • Avoid mixing print and logger in the same application unless there’s a clear distinction (e.g., print for user-facing output, logger for diagnostics).

Conclusion

Embracing logging over print is a hallmark of transitioning from beginner programming to writing professional, maintainable code. Start small by configuring a basic logger in your projects and gradually explore advanced features like multiple handlers and external integrations.