Richard Jones' Log: Debug logging only when it's needed
Thu, 16 Jul 2009
This is an idea mentioned in passing at the MXUG last night. It seemed neat to me.
This is a little snippet that'll let you sprinkle DEBUG level logging throughout your code and only have it appear in your log file when it's needed (for example when there's an exception.)
'''Capture a logger's DEBUG output for potential later dumping. To use: handler = DebugCapture(log) And at the end of a transaction: handler.clear() If an error occurs: handler.emit_debug() ''' import logging class DebugCapture(logging.Handler): def __init__(self, logger): logging.Handler.__init__(self, logging.DEBUG) self.records = [] # insert myself as the handler for the logger self.slaves = logger.handlers logger.handlers = [self] def emit_debug(self): # emit stored records to the original logger handler(s) for record in self.records: for slave in self.slaves: slave.handle(record) def clear(self): self.records = [] def emit(self, record): self.records.append(record) def handle(self, record): # if its a DEBUG level record then intercept otherwise # pass through to the original logger handler(s) if record.levelno == logging.DEBUG: return logging.Handler.handle(self, record) return sum(slave.handle(record) for slave in self.slaves) # # SAMPLE PROGRAM # logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) handler = DebugCapture(log) def task(argument): log.info('run task (argument=%s)'%argument) log.debug('debug info should only appear next to error') if argument%2: BROKEN log.debug('all done!') if __name__ == '__main__': for i in range(5): try: task(i) except: handler.emit_debug() log.exception('task is broken') handler.clear()
When run, this will generate the following console output:
$ python sample_program.py INFO:root:run task (argument=0) INFO:root:run task (argument=1) DEBUG:root:debug info should only appear next to error ERROR:root:task is broken Traceback (most recent call last): File "sample_program.py", line 59, in task(i) File "sample_program.py", line 53, in task if argument%2: BROKEN NameError: global name 'BROKEN' is not defined INFO:root:run task (argument=2) INFO:root:run task (argument=3) DEBUG:root:debug info should only appear next to error ERROR:root:task is broken Traceback (most recent call last): File "sample_program.py", line 59, in task(i) File "sample_program.py", line 53, in task if argument%2: BROKEN NameError: global name 'BROKEN' is not defined INFO:root:run task (argument=4)
You can write this as a context manager, which removes the try / except and cleanup code from the client code. An example similar to yours is here: http://code.google.com/p/blogmaker/source/browse/trunk/blogmaker/util/trap_errors.py