我正在用logging.error将Python异常消息打印到日志文件:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
是否可以打印有关异常和生成异常的代码的更详细信息,而不仅仅是异常字符串?行号或堆栈跟踪之类的东西会很棒。
我正在用logging.error将Python异常消息打印到日志文件:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
是否可以打印有关异常和生成异常的代码的更详细信息,而不仅仅是异常字符串?行号或堆栈跟踪之类的东西会很棒。
当前回答
引用
如果您的应用程序以其他方式记录日志-不使用日志模块?
现在,traceback可以用在这里。
import traceback
def log_traceback(ex, ex_traceback=None):
if ex_traceback is None:
ex_traceback = ex.__traceback__
tb_lines = [ line.rstrip('\n') for line in
traceback.format_exception(ex.__class__, ex, ex_traceback)]
exception_logger.log(tb_lines)
在Python 2中使用它: 试一试: #你的函数调用在这里 Exception as ex: _, _, ex_traceback = sys.exc_info() ex_traceback log_traceback(例) 在Python 3中使用它: 试一试: X = get_number() Exception as ex: log_traceback(特异)
其他回答
日志记录有一个好处。SiggyF的答案没有显示的异常是,你可以传递一个任意的消息,日志仍然会显示所有异常细节的完整跟踪:
import logging
try:
1/0
except ZeroDivisionError:
logging.exception("Deliberate divide by zero traceback")
默认情况下(在最近的版本中)只是将错误打印到sys. log行为。Stderr,它看起来像这样:
>>> import logging
>>> try:
... 1/0
... except ZeroDivisionError:
... logging.exception("Deliberate divide by zero traceback")
...
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
我的方法是创建一个上下文管理器,记录并引发异常:
import logging
from contextlib import AbstractContextManager
class LogError(AbstractContextManager):
def __init__(self, logger=None):
self.logger = logger.name if isinstance(logger, logging.Logger) else logger
def __exit__(self, exc_type, exc_value, traceback):
if exc_value is not None:
logging.getLogger(self.logger).exception(exc_value)
with LogError():
1/0
您可以将记录器名称或记录器实例传递给LogError()。默认情况下,它将使用基本日志记录器(通过将None传递给logging.getLogger)。 还可以简单地添加一个开关来引发错误或只记录错误。
一点点装饰处理(非常松散的灵感来自可能单子和提升)。您可以安全地删除Python 3.6类型注释,并使用较旧的消息格式化样式。
fallible.py
from functools import wraps
from typing import Callable, TypeVar, Optional
import logging
A = TypeVar('A')
def fallible(*exceptions, logger=None) \
-> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
"""
:param exceptions: a list of exceptions to catch
:param logger: pass a custom logger; None means the default logger,
False disables logging altogether.
"""
def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:
@wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except exceptions:
message = f'called {f} with *args={args} and **kwargs={kwargs}'
if logger:
logger.exception(message)
if logger is None:
logging.exception(message)
return None
return wrapped
return fwrap
演示:
In [1] from fallible import fallible
In [2]: @fallible(ArithmeticError)
...: def div(a, b):
...: return a / b
...:
...:
In [3]: div(1, 2)
Out[3]: 0.5
In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
File "/Users/user/fallible.py", line 17, in wrapped
return f(*args, **kwargs)
File "<ipython-input-17-e056bd886b5c>", line 3, in div
return a / b
In [5]: repr(res)
'None'
您还可以修改这个解决方案,从except部分返回比None更有意义的内容(甚至可以通过在fallible的参数中指定这个返回值,使解决方案具有泛型)。
引用
如果您的应用程序以其他方式记录日志-不使用日志模块?
现在,traceback可以用在这里。
import traceback
def log_traceback(ex, ex_traceback=None):
if ex_traceback is None:
ex_traceback = ex.__traceback__
tb_lines = [ line.rstrip('\n') for line in
traceback.format_exception(ex.__class__, ex, ex_traceback)]
exception_logger.log(tb_lines)
在Python 2中使用它: 试一试: #你的函数调用在这里 Exception as ex: _, _, ex_traceback = sys.exc_info() ex_traceback log_traceback(例) 在Python 3中使用它: 试一试: X = get_number() Exception as ex: log_traceback(特异)
如果您使用普通日志—所有日志记录都应该符合这条规则:一条记录=一行。遵循这条规则,您可以使用grep和其他工具来处理日志文件。
但是回溯信息是多行的。所以我的答案是zangw在这篇文章中提出的解决方案的扩展版本。问题是回溯行内部可能有\n,所以我们需要做额外的工作来消除这些行结束符:
import logging
logger = logging.getLogger('your_logger_here')
def log_app_error(e: BaseException, level=logging.ERROR) -> None:
e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
traceback_lines = []
for line in [line.rstrip('\n') for line in e_traceback]:
traceback_lines.extend(line.splitlines())
logger.log(level, traceback_lines.__str__())
在这之后(当你分析你的日志时),你可以从你的日志文件中复制/粘贴所需的回溯行,并这样做:
ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
print(line)
利润!