Sunday, November 30, 2025

Logging an uncaught exception · Ponderings of an Andy


Introduction

A nicely construct software will use logging as an alternative of print statements. An exceptionally nicely constructed one will log in such a means that extra context is added to every log message and be consumable by a log aggregation service. Maybe I will write up such an article sooner or later. For now although, let’s deal with a single drawback.

Right here is a few pattern code to exhibit the issue.

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

handler = logging.FileHandler("app.log")
formatter = logging.Formatter("%(asctime)s %(title)s %(levelname)s %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

def divide(a, b):
    return a/b

def important():
    logger.data("Utility begin")
    a = 10
    b = 0
    logger.data(divide(a,b))
    logger.data("Utility finish")

if __name__ == "__main__":
    important()

Briefly, this units up the app.log file to obtain our log messages. The important perform goes to divide two numbers, log the outcome, and finish this system. Fairly easy.

Besides, on this case, it’s dividing by zero. This throws an error and crashes this system.

The console spits out a stack hint

Traceback (most latest name final):
File "/house/andy/important.py", line 22, in <module>
    important()
    ~~~~^^
File "/house/andy/important.py", line 18, in important
    logger.data(divide(a,b))
                ~~~~~~^^^^^
File "/house/andy/important.py", line 12, in divide
    return a/b
        ~^~
ZeroDivisionError: division by zero

The app.log file incorporates a single line:

2025-07-14 22:20:04,551 __main__ INFO Utility begin

Gotcha

The place is the gotcha right here? The stack hint is correct there!

You’re, after all, proper. Nonetheless, think about that this was not a easy software, however as an alternative a manufacturing software that sends logs to a central service. Your software crashed and now one was watching the console. Your app.log file has no data. It says the applying began after which…nothing. What occurred? Is it nonetheless working?

As you dig by way of working processes, or examine a /well being finish level for responses, you discover out that it’s not working. That took a variety of time, and manufacturing is not responding.

You’ve got misplaced all visibility to what occurred in your software on the most crucial second. When it crashed and spit out a stack hint, you need as a lot element as you will get.

Resolution

The answer is sys.excepthook. That is known as when any exception is raised and uncaught, aside from SystemExit. It is fairly simple to make the most of as nicely. Just a few small modifications to the above code will permit us to log this utterly sudden ZeroDivisionError.

import logging
import sys

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

handler = logging.FileHandler("app.log")
formatter = logging.Formatter("%(asctime)s %(title)s %(levelname)s %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
    logger.essential(
        "uncaught exception, software will terminate.",
        exc_info=(exc_type, exc_value, exc_traceback),
    )
sys.excepthook = handle_uncaught_exception


def divide(a, b):
    return a/b

def important():
    logger.data("Utility begin")
    a = 10
    b = 0
    logger.data(divide(a,b))
    logger.data("Utility finish")

if __name__ == "__main__":
    important()

The essential bit is the brand new handle_uncaught_exception perform and the sys.excepthook line (with acceptable import assertion).

Somebody working this within the console will discover that there’s not a stack hint dumped to the console now. As an alternative, our log incorporates essential data:

2025-07-14 22:30:44,061 __main__ INFO Utility begin
2025-07-14 22:30:44,061 __main__ CRITICAL uncaught exception, software will terminate.
Traceback (most latest name final):
File "/house/andy/important.py", line 31, in <module>
    important()
    ~~~~^^
File "/house/andy/important.py", line 27, in important
    logger.data(divide(a,b))
                ~~~~~~^^^^^
File "/house/andy/important.py", line 21, in divide
    return a/b
        ~^~
ZeroDivisionError: division by zero

That is nice! Now when troubleshooting this failing software and looking out on the logs, we are able to simply see that an exception occurred. Moreover, with correct updates to the logging, extra context could be present such because the values of a and b. Whereas it is easy sufficient to determine that b is 0 on this easy instance, the context in a bigger manufacturing software may save a ton of troubleshooting time.

Conclusion

Logging is important to understanding what your software is doing. However, it is much more essential to figuring out why it stopped working. In case your logs aren’t offering data when the applying crashes, it is functionally ineffective. By implementing an excepthook, you’ll be able to catch and correctly log uncaught exceptions.

I do know a few of you might be arising with options. Horrible concepts like wrapping your entire important block in a strive/besides. There are official causes to throw an exception. On this case, a ZeroDivisionError is a superb exception to catch. However, you’d need to do it round as small of a code block as attainable.

This can be a clear strategy to catch really unexcepted exceptions, not one thing a developer may have anticipated and, maybe, fastened with extra enter validation.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles