TLDR; When the log record is passed an exc_info
is there a way for me to use that for the log record data instead of were it was logged from?
I am using a exceptionhook to log any uncaught exceptions. For normal logging instances I am using the data within the log record to get additional information such as the file, line number and so on. The log record will use line the error was logged from as the basis for that information. This means that when the logger within the exceptionhook logs the exception the extra log record data will point to the exceptionhook instead of the location where the original exception occurred.
Is there a way for me to use the exc_info data for the log record instead of the log call?
This is best illustrated in a two file setup. The first file raises the error
# File log_record_test.py
import logging
import log_record_setup
import sys
sys.excepthook = log_record_setup.exc_hook
logger = logging.getLogger()
logger.addHandler(log_record_setup.LogHandler())
def test():
logger.critical("Normal logging here")
def test2():
raise TypeError()
test()
test2()
The second file contains the set up, most importantly the exception hook.
# File log_record_setup.py
import logging
logger = logging.getLogger("TestLogger")
class LogHandler(logging.Handler):
def __init__(self):
super(LogHandler, self).__init__()
def emit(self, record):
self.format(record)
log_record_dict = record.__dict__.copy()
print(f"{log_record_dict['filename']} at {log_record_dict['lineno']} within {log_record_dict['funcName']}")
def exc_hook(exc_type, exc_value, tb):
logger.critical("Error happened", exc_info=(exc_type, exc_value, tb))
If the log_record_test.py
file now runs we get this output.
log_record_test.py at 12 within test
log_record_setup.py at 18 within exc_hook
Where the first test function has the output we want, but the second test that raises an exception says that the it originates from the exception hook. This is the part I would like to say that it originated from where the TypeError
was raised within the example.
You can use the stacklevel keyword argument to specify which frame in the call stack to use for computing the line number and file name. Use stacklevel=2
in this case since you want to use the immediate caller of the frame that calls the logger:
def exc_hook(exc_type, exc_value, tb):
logger.critical("Error happened", stacklevel=2, exc_info=(exc_type, exc_value, tb))
1