views:

72

answers:

3

Hello--

I have a long-running program on a remote machine and want to be sure that (1) I have a record of any exception that causes it to terminate and (2) someone is notified if it terminates. Does anyone see drawbacks to the method I am using? (or have recommendations for a better one?)

I've read the Python docs and many exception-related posts here, and understand that blanket except clauses are usually a bad idea. Within subroutines and modules I always use except to handle specific expected exceptions, but it seems useful to have a "catch-all" except clause at the highest level of the program to ensure I can log the exception before the program exits.

What do you think?

import traceback
try:
    # main program code here
except BaseException:
    tb = traceback.format_exc()
    msg = "Exiting program due to exception:" + tb
    LogToFile(msg)         # custom logging function
    SendAlertEmail(msg)    # warn admin that program terminated
    raise                  # program exits with the existing exception

Note that I was using BaseException instead of Exception because if someone at the terminal presses Ctrl-C, I would like to log that as the reason for exiting the program (and alert an admin that the program was exited). But I suppose I could also use:

except Exception, KeyboardInterrupt:
+6  A: 

There is no specific drawback, but there is an excellent alternative -- sys.excepthook.

In your specific version, consider using a bare except:, and sys.exc_info() to get the exception information; that will ensure you do catch everything -- even in the weird case where some module raises something else than an instance of a subclass of BaseException. E.g.:

>>> class X: pass
... 
>>> raise X
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.X: <__main__.X instance at 0xc9ad0>

As you see, it is still possible to raise something a except BaseException: would not catch -- that's why bare-except except: still exists (specifically for very special uses such as yours!).

Whether you use the hook, or build your own, consider (perhaps depending on configuration flags or environment settings) not burdening the end-user with all the details (just as a neat touch of improved user experience!), just a meaningful summary (reassuring the user that all details of the problem have been recorded, etc, etc).

Alex Martelli
Thanks for the quick response and useful comments, and good point about not all module exceptions necessarily inheriting from BaseException. (p.s. I'm a brand new user so I don't seem to be able to upvote your answer yet)
emdash
@emdash, yep, you need I think 15 rep to upvote (but you can always _accept_ the answer that has most helped you -- after a while to see if others are coming, of course -- and get started on the rep earning threadmill, since you get some rep for accepts, too, though the accepted answerer gets more).
Alex Martelli
+2  A: 

Consider what happens if the exception is due to lack of diskspace. If the logging is written to the same partition

LogToFile(msg)

will raise an exception, an so no email is sent either. A simple try/except around each will avoid that problem

tb = traceback.format_exc()
msg = "Exiting program due to exception:" + tb
try:
    LogToFile(msg)         # custom logging function
except:
    pass
try:
    SendAlertEmail(msg)    # warn admin that program terminated
except:
    pass
raise                  # program exits with the existing exception
gnibbler
Why the downvote? This is an important case to handle.
fmark
@gnibbler Thanks, good point. And if I wanted to have a record of every exception, I could additionally email/save any exceptions that occur in the process of saving/emailing the first exceptions.
emdash
@fmark, I've had a bunch of driveby downvotes lately. Maybe they are from the same person. Perhaps they didn't like the naked `except:`. No way of knowing...
gnibbler
A: 

"I've read the Python docs and many exception-related posts here, and understand that blanket except clauses are usually a bad idea."

The rationale for that is that you might be expecting three kinds of exception then wind up swallowing the fourth kind that you never considered was a possibility. A handler that does an unconditional re-raise eliminates this concern -- you will be seeing unexpected exceptions -- so it's a perfectly cromulent exception to the general rule.

Russell Borogove