A ReportingLogger class for Lokai
Now that we have looked at logging requirements and the logging module it’s time to look at some code. This is the basic Logger class that I use. It provides statistics recording for making commit/rollback decisions, and it provides a method for executing handler methods from the logger instance. This is quite powerful and allows the calling program to interact with handlers without knowing the detail of what handlers actually exist.
We start in the obvious way by inheriting from Logger class. We initialise the statistics, and inhibit propagation up the hierarchy. Inhibiting propagation is open to debate. In my case it has the effect of limiting all of my file related actions to the lower level of the hierarchy, and leaves the root level to flush output when the program exits.
class RecordingLogger(logging.Logger): def __init__(self, name, level=logging.NOTSET): logging.Logger.__init__(self, name=name, level=level) self._set_stats() self.propagate = 0
_set_stats
initialises the level accumulators
def _set_stats(self): self.stats = {'CRITICAL': 0, 'ERROR': 0, 'WARNING': 0, 'INFO': 0, 'MONITOR': 0, 'DEBUG': 0}
Overwrite _log
just to add a line that accumulates the statistics. Note that it uses the message level name rather than the level value.
def _log(self, level, msg, args, exc_info=None, extra=None): self.stats[logging.getLevelName(level)] += 1 logging.Logger._log(self, level=level, msg=msg, args=args, exc_info=exc_info, extra=extra)
execute
is a key method that passes a function name and some arguments to all handlers that the logger can find. I use it for flushing buffers and setting email subject text. It can be used to execute any function you might want to define on a handler. The method is actually an iterator that returns any response that it finds from each execution. The method execute_all
assumes you don’t care about the returns and simply executes everything.
def execute(self, function, *args, **kwargs): """ Apply this function to all the handlers for this logger. Ignore if the handler does not support the function. This supports return values by yielding the answer at each level. """ c = self while c: for hdlr in c.handlers: if hasattr(hdlr, function): target = getattr(hdlr, function) if callable(target): yield target(*args, **kwargs) if not c.propagate: c = None #break out else: c = c.parent def execute_all(self, function, *args, **kwargs): """ call execute in a loop and throw away any responses. """ for resp in self.execute(function, *args, **kwargs): pass
Add a flush
method that flushes all findable handlers and resets the statistics.
def flush(self): self.execute_all('flush') self._set_stats()
And then we set this to be the default logger
logging.setLoggerClass(RecordingLogger)
And that’s it, really. The next step is to look at a handler and see what goes on there.
Category: Development | Tags: logging, lokai, python Comments Off on A ReportingLogger class for Lokai