Levels
This module is nice in that it doesn't require any other libraries other than the built-in time
module.
There is a list that defines the levels: the value and a name. That's used to convert values to names, as well as create a global variable for each level. They can be used directly as, for example, logging.ERROR
.
import time levels = [(0, 'NOTSET'), (10, 'DEBUG'), (20, 'INFO'), (30, 'WARNING'), (40, 'ERROR'), (50, 'CRITICAL')] for value, name in levels: globals()[name] = value def level_for(value): """Convert a numberic level to the most appropriate name. :param value: a numeric level """ for i in range(len(LEVELS)): if value == LEVELS[i][0]: return LEVELS[i][1] elif value < LEVELS[i][0]: return LEVELS[i-1][1] return LEVELS[0][1]
Getting a Logger
To get hold of a logger, you use the getLogger
function. You pass it the name of the logger you want to create or retrieve. This way you can ask for a logger anywhere in your code. Specifying the same name will get you the same logger.
logger_cache = dict() def getLogger(name): """Create or retrieve a logger by name. :param name: the name of the logger to create/retrieve """ if name not in logger_cache: logger_cache[name] = Logger() return logger_cache[name]
Logger
The core of the module is the Logger
class. By default loggers use a PrintHandler
(which we'll look at below) that simply uses print
to output the messages. To change that to a different handler use the addHandler
method. The method is called addHandler
to be closer to CPython's logger. It works slightly differently in that it actually adds an additional handler the the logger rather than replacing it.
Logger
as a level
property that allows you to get and set the cuttoff priority level. Messages with a level below the one set are ignored.
Finally, there is the log method that is the core of the class. This takes the level to log at, a format string, and arguments to be inserted into the format string. The %
operator is used (passing it the supplied arguments) to create the message.
class Logger(object): """Provide a logging api.""" def __init__(self): """Create an instance. :param handler: what to use to output messages. Defaults to a PrintHandler. """ self._level = NOTSET self._handler = PrintHandler() def setLevel(self, value): """Set the logging cuttoff level. :param value: the lowest level to output """ self._level = value def addHandler(hldr): """Sets the handler of this logger to the specified handler. *NOTE* this is slightly different from the CPython equivalent which adds the handler rather than replaceing it. :param hldr: the handler """ self._handler = hldr def log(self, level, format_string, *args): """Log a message. :param level: the priority level at which to log :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ if level >= self._level: self._handler.emit(level, format_string % args)
Finally, there is a convenience method for logging at each level.
def debug(self, format_string, *args): """Log a debug message. :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ self.log(DEBUG, format_string, *args) def info(self, format_string, *args): """Log a info message. :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ self.log(INFO, format_string, *args) def warning(self, format_string, *args): """Log a warning message. :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ self.log(WARNING, format_string, *args) def error(self, format_string, *args): """Log a error message. :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ self.log(ERROR, format_string, *args) def critical(self, format_string, *args): """Log a critical message. :param format_string: the core message string with embedded formatting directives :param args: arguments to ``format_string.format()``, can be empty """ self.log(CRITICAL, format_string, *args)
Handlers
We skipped over that part of the file. And what is that PrintHandler
we saw in the constructor?
Looking at Logger
's log
method above, we see that the handler object is used to emit (i.e. send out) the message. The format_string
and args
are combined using the %
operator and the result is sent, along with the level, to the emit
method of the handler.
Here's the builtin PrintHandler
along with the LoggingHandler
abstract base class*.
LoggingHandler
provides a method, format
, which takes the level and message to be logged and returns the string to be output, built from a timestamp, the name of the level, and the message.
It also contains a placeholder for the emit
method which raises a NotImplementedError
as this method must be implemented by subclasses.
class LoggingHandler(object): """Abstract logging message handler.""" def format(self, level, msg): """Generate a timestamped message. :param level: the logging level :param msg: the message to log """ return '{0}: {1} - {2}'.format(time.monotonic(), level_for(level), msg) def emit(self, level, msg): """Send a message where it should go. Place holder for subclass implementations. """ raise NotImplementedError()
PrintHandler
subclasses LoggingHandler
and provides an implementation of emit
which uses LoogingHandler
's format
method to create the string to be output and prints it. This handler is bundled into the logging module since this is usually what you will need.
class PrintHandler(LoggingHandler): """Send logging messages to the console by using print.""" def emit(self, level, msg): """Send a message to teh console. :param level: the logging level :param msg: the message to log """ print(self.format(level, msg))
*An abstract base class is not meant to be directly instantiated, rather it is to be subclassed.
Text editor powered by tinymce.