pythonpython-3.xexception

How to log a Python 3 exception, but without its stack trace?


When I want to log some specific Exception, but otherwise ignore it, I can do that like so:

import logging

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError:
    logger.error("It failed with:", exc_info=True)

(This is in fact an MRE, as something_that_may_fail hasn't been defined, so the try block will raise NameError with message name 'something_that_may_fail' is not defined. 😉)

This however will also log the stack trace:

It failed with:
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
NameError: name 'something_that_may_fail' is not defined

Sometimes that isn't what I want: In some cases, I already know that exception type and exception message (together with my custom log message) will suffice, and don't want to expand the log with stack traces that don't tell me anything new. So I'd want a log entry that simply is

It failed with:
NameError: name 'something_that_may_fail' is not defined

I can achieve that by passing a 3-tuple as the exc_info, with the stack trace component replaced by None:

import logging
import sys

logger = logging.getLogger(__name__)

try:
    something_that_may_fail()
except NameError:
    exc_type, exc_value, _trace = sys.exc_info()
    logger.error("It failed with:", exc_info=(exc_type, exc_value, None))

But I'm not sure how reliable that is. (The documentation doesn't mention how the tuple may or may not deviate from one returned by sys.exc_info().)

Examining the exception myself with

...
except NameError as e:
    ...

comes with its own problems:

So what is the proper way to log exception type and message without the stack trace? Is there a cleaner way than my make-the-trace-None hack above?


Solution

  • traceback.format_exception_only can be used for that:

    import logging
    import sys
    import traceback
    
    logger = logging.getLogger(__name__)
    
    try:
        something_that_may_fail()
    except NameError:
        exc_type, exc_value, _trace = sys.exc_info()
        exc_desc_lines = traceback.format_exception_only(exc_type, exc_value)
        exc_desc = ''.join(exc_desc_lines).rstrip()
        logger.error(f"It failed with:\n{exc_desc}")
    

    or without sys:

    import logging
    import traceback
    
    logger = logging.getLogger(__name__)
    
    try:
        something_that_may_fail()
    except NameError as e:
        exc_desc_lines = traceback.format_exception_only(type(e), e)
        exc_desc = ''.join(exc_desc_lines).rstrip()
        logger.error(f"It failed with:\n{exc_desc}")
    

    (Found this by looking how the logging module actually extracts and formats information from exc_info. There traceback.print_exception is being used, so I looked what else is available in the traceback module.)