Serious applications not only use a database (see Databases, GUIs, and Python), but they also need to log information. Too often, logging is seen as merely spouting debug information onto the console, with print statements, most likely. There is far more to logging than that: Logging is necessary to keep an audit trail of an application, monitor performance, and calculate usage statistics.
Python's new logging package owes a large debt to log4j, the well-regarded logging package for Java from the Apache project, but it isn't a direct translation from Java to Python. That's good because log4j can be a pain to work with. The logging module is thread-safe and very easy to use.
In fact, starting with the new logging module is simplicity itself: You can use just use it out of the box:
>>> import logging >>> logging.warn("WARNING")
The logging module is quite flexible and offers a large number of handlersthat is, modules that can send you logging information to all sorts of destinations, such as the console, a text file, and also the NT event log or UNIX's SysLog.
A real-world example of logging takes a bit more thought. You must determine your logging needs quite precisely before adding logging to your application. Python's logging module allows enables to create a hierarchy of loggers objects, with every logger having its own level.
For instance, a subsystem of your application can be responsible for handling requests. These requests need to go through two filters before being processed. In this case, you can have the following hierarchy:
requestLogger requestLogger.filter1 requestLogger.filter2
You get a Logger object by asking the logging module for it, using the getLogger(name) function. The same name returns always the same object; if the name is not yet used, a new Logger object is created.
You can choose from many different Handler objects. The following example uses the SMTP handler to mail me a critical error that my script is about to finish:
import logging from logging import handlers logger = logging.getLogger("requestLogger") logger.setLevel(logging.CRITICAL) logger.addHandler(handlers.SMTPHandler("calcifer", "logger@calcifer", ["boud"], "CRITICAL error")) logger.critical("Script about to finish")
The logging module works internally with LogRecord objects. You can create Formatter to create a custom string representation of these LogRecords. You can then set your formatter in the Handler object that you are using with setHandler. Be sure, however, that your formatter is optimizedotherwise, you might get a severe performance penalty from logging.
Filters can be created to limit the output of your logging system. They work on the name of your Logger object and can thus be configured to allow logging from only part of your hierarchy. You can add filters to both handlers and loggers, giving you a lot of flexibility.
Finally, when your logging system is in place, you will often want to allow the users of your application to fine-tune the information they receive. The logging module allows for configuration through a file, using the fileConfig class. The file format is simple enough, but the actual range of options can be bewildering, just as with log4j.