I'm working on implementing logging into a custom Python package. Throughout the package, exceptions can be raised if inputs are unacceptable, or if tests fail, etc. I'd like to log any exceptions that are raised within the package using the built-in logging
module.
So far, I've seen a few solutions. The simplest is wrapping any code that could raise an Exception with a try-catch block:
import logging
try:
raise Exception
except Exception as e:
logging.error(e)
raise
This would be acceptable if there were only a few places where assert
is used or exceptions can be otherwise be thrown within the package. At the scale of the package, wrapping every line that could raise an Exception is way too burdensome.
I've also seen solutions involving overriding sys.excepthook
with a custom function (see here), which seems to work decently. However, setting sys.excepthook
will cause any Exception raised by an end-user's code to also log all errors, which is not ideal.
# Some end-user's program
import my_custom_package
raise Exception # This gets logged!
Ideally, I want a solution that logs every exception that arises strictly within the package, without modifying lines that would raise Exceptions, and without changing global behavior that affects end-users.
IMPLEMENTED SOLUTION:
# __init__.py
# Set up logging
logger = logging.getLogger(__name__)
# Override exception handler
def _exception_handler(exc_type, exc_value, exc_traceback):
# Handle exception normally if it's a KeyboardInterrupt
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
# Handle exception normally if error originates outside of library
exc_filepath = Path(traceback.extract_tb(exc_traceback)[-1].filename)
if not Path(__file__).parent in exc_filepath.parents:
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
# Log error if it comes from library
logger.error("uncaught exception", exc_info=(
exc_type, exc_value, exc_traceback))
sys.excepthook = _exception_handler