Wrap the logging information around a context manager, like this though you can easily change the details to meet your requirements:
import traceback
# This is a context manager
class LogError(object):
def __init__(self, logfile, message):
self.logfile = logfile
self.message = message
def __enter__(self):
return self
def __exit__(self, type, value, tb):
if type is None or not issubclass(type, Exception):
# Allow KeyboardInterrupt and other non-standard exception to pass through
return
self.logfile.write("%s: %r\n" % (self.message, value))
traceback.print_exception(type, value, tb, file=self.logfile)
return True # "swallow" the traceback
# This is a helper class to maintain an open file object and
# a way to provide extra information to the context manager.
class ExceptionLogger(object):
def __init__(self, filename):
self.logfile = open(filename, "wa")
def __call__(self, message):
# override function() call so that I can specify a message
return LogError(self.logfile, message)
The key part is that __exit__ can return 'True', in which case the exception is ignored, and the program continues to carry on. The code also needs to be a bit careful, since a KeyboardInterrupt (control-C), SystemExit, or other non-standard exception might be raised, and where you actually do want the program to stop.
You can use the above in your code like this:
elog = ExceptionLogger("/dev/tty")
with elog("Can I divide by 0?"):
1/0
for i in range(-4, 4):
with elog("Divisor is %d" % (i,)):
print "5/%d = %d" % (i, 5/i)
That snippet gives me the output:
Can I divide by 0?: ZeroDivisionError('integer division or modulo by zero',)
Traceback (most recent call last):
File "exception_logger.py", line 24, in <module>
1/0
ZeroDivisionError: integer division or modulo by zero
5/-4 = -2
5/-3 = -2
5/-2 = -3
5/-1 = -5
Divisor is 0: ZeroDivisionError('integer division or modulo by zero',)
Traceback (most recent call last):
File "exception_logger.py", line 28, in <module>
print "5/%d = %d" % (i, 5/i)
ZeroDivisionError: integer division or modulo by zero
5/1 = 5
5/2 = 2
5/3 = 1
I think it's also easy to see how one might modify the code to handle logging only IndexError exceptions, or even to pass in the base exception type to catch.