Beyond print()
print()
is a great quick and dirty way to log to the console in python. Even senior developers will use print() all the time. For anything more than a quick app you should be using a real logging module.
There is nothing print() can do that logging can't do better.
Python already includes a fantastic logging module built-in!
Why use the logging module?
The logging module allows you to set different level of logging based on the event you are logging. If you use an external log collection tool this becomes invaluable. Syslog and Papertrail are two great examples of logging services that functionality depends on using intelligent logging levels throughout your code.
Want to be notified automatically when there is an error in your code?
No need to write any code to do this, just use CRITICAL, WARNING, ERROR log levels in your code and tools like Papertrail can automatically alert you without changes to your code.
Using the logging module allows you to use different log levels for events and only display what you are interested in. If down the road you need to troubleshoot something, you can turn on DEBUG level logging. If done properly, you will already have DEBUG level logging events in your code base.
Logging levels
- CRITICAL
- ERROR
- WARNING
- INFO
- DEBUG
- NOTSET
While coding, you can insert DEBUG level events for areas of code that might be important to troubleshoot down the line but hide these messages by setting the log level to INFO or higher.
Many modules use python's logging module. This will allow you to get log events from other modules that may be critical to troubleshooting.
How to use logging in your application
I am not going to cover the entire logging module, this is an introduction to show how easy it is to use and why you should be using the logging module instead of print().
If you want more information I recommend going through the documentation for the logging module.
Before you can use the logging module, you need to import it.
import logging
After importing the logging module, you need to configure it. There are three ways to configure the logging module:
- Ini File
- Dictionary
- Code
I am only going to cover using code, the other two methods are easy to use and you can find details in the above documentation.
The most basic logging configuration
You are able to start logging with only one line of setup code.
logging.basicConfig(level=logging.INFO)
At this point, you can start logging.
logging.info('Connected to super cool database')
logging.debug('Connection failed to super cool database')
Quiz: What will be displayed?
Only the first line will be displayed, when we set up the logger we set the default logging level to be INFO and DEBUG level events will not be displayed.
Logger basic config
In many applications, you will use the basicConfig() function to further configure the logging module. This is typically used to customize the format of log entries.
import logging
logging.basicConfig(format='%(process)d-%(levelname)s-%(message)s')
Use the logging module documentation if you need a more complex formatting setup.
Getting a logger
Up to now, we have been using the root logger
. It is common for an application to have one or more custom loggers.
Getting a custom logger is as simple as specifying the name.
logger = logging.getLogger('my_logger')
It is very common to use the __name__
variable to create a custom logger. This will create a new logger with the name of the module.
``logger = logging.getLogger('name')`
When using a custom logger, you need to use this object to log events or you will be using the root logger again.
logger = logging.getLogger('__name__')
logger.info('This is using my custom logger')
logging.info('This is using the root logger')
Handlers
While the default logging interface works for most things, there are situations you will want to use log handlers.
Log handlers allow you to log to multiple locations at once, log to email, or even a stream each with their own default levels.
import logging
logger = logging.getLogger('__name__')
log_stream_handler = logging.StreamHandler()
log_file_handler = logging.FileHanlder('log.txt')
log_stream_handler.setLevel(logging.INFO)
log_file_handler.setLevel(logging.DEBUG)
logger.addHandler(log_stream_handler)
logger.addHanlder(log_file_handler)
Any log events you create using the logger
object will be written to the console and added to the log file. The log file will capture all log events down to DEBUG where the console will only show INFO level events.
Conclusion
This covers most of the core functionality of the logging module, but it isn't everything. Remember to check the documentation if you have any questions. Most larger applications you are going to want to have console and file logging with different levels and custom formatting.
You are also going to want the end user to be able to configure logging settings from a configuration file. While the logging module has the ability to load settings from a configuration file, you will likely already have a configuration file that you will want to specify log level there and just create the logging objects from your own custom configuration object.