私はlogging.error
を使ってPython例外メッセージをログファイルに出力しています。
import logging
try:
1/0
except ZeroDivisionError as e:
logging.error(e) # ERROR:root:division by zero
例外文字列だけでなく、例外とそれを生成したコードに関するより詳細な情報を印刷することは可能ですか?行番号やスタックトレースのようなものは素晴らしいでしょう。
logger.exception
はエラーメッセージとともにスタックトレースを出力します。
例えば:
import logging
try:
1/0
except ZeroDivisionError as e:
logging.exception("message")
出力:
ERROR:root:message
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
@ Paulo Check 注、「Python 3ではexcept
部分のすぐ内側でlogging.exception
メソッドを呼び出す必要があることに注意してください。奇妙な例外が発生する可能性がある場所です。ドキュメントはそれについて警告しています。」
SiggyFの答え が表示されないというlogging.exception
のいいところは、あなたが任意のメッセージを渡すことができることです。
import logging
try:
1/0
except ZeroDivisionError:
logging.exception("Deliberate divide by zero traceback")
デフォルトの(最近のバージョンでは)エラーをsys.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
エラーレベルを選択できるようにするには、exc_infoオプションを使用することをお勧めします(exception
を使用する場合は、常にerror
が表示されます)。
try:
# do something here
except Exception as e:
logging.fatal(e, exc_info=True) # log exception info at FATAL log level
logging
モジュールを使用しないで、アプリケーションが他の方法でログを取る場合はどうなりますか?
さて、ここで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で使用してください。
try:
# your function call is here
except Exception as ex:
_, _, ex_traceback = sys.exc_info()
log_traceback(ex, ex_traceback)
Python 3で使用してください。
try:
x = get_number()
except Exception as ex:
log_traceback(ex)
プレーンログを使用する場合 - すべてのログレコードはこの規則に一致する必要があります:one record = one line
。この規則に従って、あなたはあなたのログファイルを処理するために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)
利益!
この答えは上記の優れたものから成り立っています。
ほとんどのアプリケーションでは、logging.exception(e)を直接呼び出すことはありません。たぶんあなたはこのようにあなたのアプリケーションまたはモジュールに特定のカスタムロガーを定義しました:
# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)
# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)
この場合、ロガーを使用して次のように例外(e)を呼び出します。
try:
1/0
except ZeroDivisionError, e:
my_logger.exception(e)
ほんの少しのデコレータ処理(たぶんモナドと持ち上げに触発された) 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'
この解決法を修正して、None
部分からexcept
よりも少し意味のある何かを返すようにすることもできます(あるいは、この戻り値をfallible
の引数に指定することによって、解決策を一般化することさえできます)。