Python - 如何在新线程中获取堆栈跟踪

Python - How to get stack trace in a new thread

logger.exception 如果在池中执行并且错误消息作为 arg 传递,则不会显示堆栈跟踪。

设置:

import logging
from concurrent.futures import ThreadPoolExecutor
logger = logging.getLogger('custom')
ch = logging.StreamHandler()
logger.addHandler(ch)
logger.setLevel(logging.INFO)
executor = ThreadPoolExecutor(3)

不显示堆栈跟踪:

try:
    1/0
except Exception as e:
    executor.submit(logger.exception, e)

显示堆栈跟踪:

try:
    1/0
except Exception as e:
    executor.submit(logger.exception(e))

此代码有效,因为问题在提交到线程池之前已被记录:

try:
    1/0
except Exception as e:
    executor.submit(logger.exception(e))

实际上,您发送到线程池的是 None

这是行不通的,因为在 logger.exception 调用中发生了一些神奇的事情,当它在异常处理上下文之外时不起作用(当它处于 运行在另一个线程中):

try:
    1/0
except Exception as e:
    executor.submit(logger.exception, e)

查看文档通常会有所帮助。对于 .exception() method 文档说:

Logs a message with level ERROR on this logger. The arguments are interpreted as for debug(). Exception info is added to the logging message. This method should only be called from an exception handler.

.debug() 文档的相关部分是:

logging.debug(msg, *args, **kwargs)

Logs a message with level DEBUG on the root logger. The msg is the message format string, and the args are the arguments which are merged into msg using the string formatting operator. (Note that this means that you can use keywords in the format string, together with a single dictionary argument.)

There are three keyword arguments in kwargs which are inspected: exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message. If an exception tuple (in the format returned by sys.exc_info()) is provided, it is used; otherwise, sys.exc_info() is called to get the exception information.

所以,这一行:

executor.submit(logger.exception, e)

将在处理日志记录的线程中调用原因 sys.exc_info(),它没有异常信息——所以没有回溯记录。相反,你想要:

executor.submit(logger.exception, "error occurred", exc_info=sys.exc_info())

所以在它的最终形式中,它看起来像:

try:
    1/0
except Exception as e:
    executor.submit(logger.exception, "error occurred", exc_info=sys.exc_info())

更好的方法是避免调用 logger.exception(),而只使用 logger.error()

try:
    1/0
except Exception as e:
    executor.submit(logger.error, "error occurred", exc_info=sys.exc_info())

如果您希望异常消息作为日志消息,您可以这样做(类似于您在原始代码中所做的):

try:
    1/0
except Exception as e:
    executor.submit(logger.error, e, exc_info=sys.exc_info())

e 将转换为字符串并用作记录回溯的消息。