Making a logging handler that produces context.

Peter Otten __peter__ at web.de
Mon Jan 14 13:38:12 CET 2013


Antoon Pardon wrote:

> I have some in house code for which I am considering replacing the
> logging code with something that uses the logging module.

> However there is one thing the in-house log code does, that seems
> difficult to do with the logging module, provide some context. The 
> in-house handlers give the possibilty to specify the number of lines of
> context the hander can provide.

> So the following code:

> Logger(fl = stderr, level = warning, context = 2)
> 
> log(INFO, "line 1")
> log(INFO, "line 2")
> log(INFO, "line 3")
> log(INFO, "line 4")
> log(WARNING, "line 5")
> 
> Will sent something like the following lines to stderr:
> 
> INFO: line 3
> INFO: line 4
> WARNING: line 5
> 
> I tried the code below, but that produced the same
> as the ordinary StreamHandler.
> 
> class ContextStreamHandler (StreamHandler):
> 
>     def __init__(self, stream=None, context = 5):
>         self.recqueue = deque([], context)
>         StreamHandler.__init__(self, stream)
>     #__init__
> 
>     def handle(self, record):
>         print("CONTEXT HANDLER")
>         rv = self.filter(record)
>         if rv:
>             self.acquire()
>             try:
>                 for rec in self.recqueue:
>                     self.emit(rec)
>                 self.recqueue.clear()
>                 self.emit(record)
>             finally:
>                 self.release
>         else:
>             self.recqueue.append(record)
>         return rv
>     #handle
> #ContextStreamHandler

It turns out the logic of the above is correct. The problem is that the 
handler has to see the INFO-level records while the filter() method has to 
reject them. The following configuration seems to achieve that:

import logging
from collections import deque

class ContextStreamHandler(logging.StreamHandler):
    def __init__(self, stream=None, context=5):
        self.record_queue = deque([], context + 1)
        logging.StreamHandler.__init__(self, stream)
    def handle(self, record):
        rv = self.filter(record)
        self.record_queue.append(record)
        if rv:
            self.acquire()
            try:
                for record in self.record_queue:
                    self.emit(record)
                self.record_queue.clear()
            finally:
                self.release()
        return rv

class LevelFilter(logging.Filter):
    def __init__(self, level, name=""):
        logging.Filter.__init__(self, name)
        self._filter_level = level
    def filter(self, record):
        return record.levelno >= self._filter_level

if __name__ == "__main__":
    root = logging.getLogger()
    root.setLevel(logging.INFO)

    handler = ContextStreamHandler(context=2)
    handler.addFilter(LevelFilter(logging.WARN))

    root.addHandler(handler)

    for i in range(20):
        if i % 5:
            root.info("message #%d" % i)
        else:
            root.warn("MESSAGE #%d" % i)





More information about the Python-list mailing list