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 foundWhen 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
- Quick Debugging: When you’re testing small code snippets or debugging simple issues,
printstatements can help you quickly inspect variable values or the flow of execution. - Educational or Experimental Code: In tutorials, learning materials, or interactive sessions,
printis often used for its simplicity. - User-Focused Output: If the output is intended directly for the user, such as in command-line interfaces or small scripts,
printis appropriate (for example in ourHello, world!context)
Limitations of print
- Lack of Control: You cannot easily suppress or redirect
printoutput 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,
printstatements 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
- Production Applications: Loggers provide detailed, timestamped messages that are crucial for monitoring and debugging in production environments.
- Granular Message Control: Loggers support different levels of severity (DEBUG, INFO, WARNING, ERROR, CRITICAL), allowing you to filter and prioritize messages.
- Output Customization: Loggers can output to various destinations (console, files, external systems) and can be formatted with timestamps, log levels, and other metadata.
- Concurrent Applications: In multi-threaded or distributed applications, the
loggingmodule 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
| Feature | print | logger |
|---|---|---|
| Severity Levels | None | DEBUG, INFO, WARNING, ERROR, CRITICAL |
| Configuration | Minimal | Handlers, formatters, and runtime control |
| Scalability | Limited | Scales Well |
| Output Destinations | Console only | Console, files, external systems |
| Thread Safety | No | Yes |
Best Practices
- Use
printfor quick debugging or simple scripts. - Switch to
loggerfor production code, especially if the application needs robust monitoring or error tracking. - Avoid mixing
printandloggerin the same application unless there’s a clear distinction (e.g.,printfor user-facing output,loggerfor 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.