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:
It is only good practice to catch a specific exception if it can actually be handled by the catch block. Very often, programmers assume that the fault can be handled at all, close to the point of occurrence. This is often wrong.
This matches the type of exception that was thrown, so the runtime system ends its search for an appropriate exception handler. Now that the runtime has found an appropriate handler, the code in that catch block is executed. After the exception handler executes, the runtime system passes control to the finally block.
If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException: ). A good rule of thumb is to limit use of bare 'except' clauses to two cases: If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.
Also the data layer is only responsible for requesting data, not for handling errors that occure while doing this. This is what meant by "throw early". In your example the catching layer is the service layer. The service itself is a new layer, sitting over the data access layer. So you want to catch the exception there.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With