Pythonエラーをログに記録するにはどうすればよいですか?
try:
do_something()
except:
# How can I log my exception here, complete with its traceback?
logging.exception
fromをexcept:
ハンドラーと共に使用して、メッセージを先頭に付けて現在の例外をログに記録します。
import logging
LOG_FILENAME = '/tmp/logging_example.out'
logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG)
logging.debug('This message should go to the log file')
try:
run_my_stuff()
except:
logging.exception('Got exception on main handler')
raise
ログファイル、/tmp/logging_example.out
を見てください:
DEBUG:root:This message should go to the log file
ERROR:root:Got exception on main handler
Traceback (most recent call last):
File "/tmp/teste.py", line 9, in <module>
run_my_stuff()
NameError: name 'run_my_stuff' is not defined
exc_info
オプションを使用すると、警告またはエラータイトルのままになります。
try:
# coode in here
except Exception as e:
logging.error(e, exc_info=True)
私の仕事は最近、アプリケーションからのすべてのトレースバック/例外を記録することを私に任せました。上記のような他の人がオンラインで投稿したものの、異なるアプローチに落ち着いた数多くのテクニックを試しました。 traceback.print_exception
をオーバーライドしています。
http://www.bbarrows.com/ に書き込みがあります。これは読みやすくなりますが、ここにも貼り付けます。
ソフトウェアが実際に遭遇する可能性のあるすべての例外をログに記録するタスクを課したとき、python例外トレースバックをログに記録するためにさまざまな手法を試しました。最初は、pythonシステム例外フック、sys.excepthookがロギングコードを挿入するのに最適な場所になると考えました。私は次のようなものを試していました:
import traceback
import StringIO
import logging
import os, sys
def my_excepthook(excType, excValue, traceback, logger=logger):
logger.error("Logging an uncaught exception",
exc_info=(excType, excValue, traceback))
sys.excepthook = my_excepthook
これはメインスレッドでは機能しましたが、プロセスが開始した新しいスレッドにはsys.excepthookが存在しないことがすぐにわかりました。ほとんどすべてがこのプロジェクトのスレッドで発生するため、これは大きな問題です。
グーグルでたくさんのドキュメントを読んだ後、私が見つけた最も役立つ情報はPython Issue trackerからでした。
スレッドの最初の投稿は、スレッド間で持続しないsys.excepthook
の動作例を示しています(以下を参照)。どうやらこれは予想される動作です。
import sys, threading
def log_exception(*args):
print 'got exception %s' % (args,)
sys.excepthook = log_exception
def foo():
a = 1 / 0
threading.Thread(target=foo).start()
このPython Issueスレッドのメッセージは、実際に2つの推奨されるハックをもたらします。 Thread
をサブクラス化して、例外をキャッチしてログに記録するためにブロックを除く独自のtryでrunメソッドをラップするか、または例外をブロックしてログに記録するthreading.Thread.run
モンキーパッチを実行します.
Thread
をサブクラス化する最初の方法は、ログスレッドを作成したいすべての場所でカスタムThread
クラスをインポートして使用する必要があるため、コードがエレガントではないようです。コードベース全体を検索し、すべての通常のThreads
をこのカスタムThread
に置き換える必要があるため、これは面倒です。ただし、このThread
が何をしているのかは明確であり、カスタムロギングコードに問題が発生した場合、誰かが診断およびデバッグしやすくなります。カスタムロギングスレッドは次のようになります。
class TracebackLoggingThread(threading.Thread):
def run(self):
try:
super(TracebackLoggingThread, self).run()
except (KeyboardInterrupt, SystemExit):
raise
except Exception, e:
logger = logging.getLogger('')
logger.exception("Logging an uncaught exception")
モンキーパッチthreading.Thread.run
の2番目の方法は、__main__
の直後に一度だけ実行し、すべての例外でロギングコードをインスツルメントすることができるため、素晴らしいです。モンキーパッチは、予想される機能を変更するため、デバッグするのが面倒です。 Python Issue trackerからの推奨パッチは次のとおりです。
def installThreadExcepthook():
"""
Workaround for sys.excepthook thread bug
From
http://spyced.blogspot.com/2007/06/workaround-for-sysexcepthook-bug.html
(https://sourceforge.net/tracker/?func=detail&atid=105470&aid=1230540&group_id=5470).
Call once from __main__ before creating any threads.
If using psyco, call psyco.cannotcompile(threading.Thread.run)
since this replaces a new-style class method.
"""
init_old = threading.Thread.__init__
def init(self, *args, **kwargs):
init_old(self, *args, **kwargs)
run_old = self.run
def run_with_except_hook(*args, **kw):
try:
run_old(*args, **kw)
except (KeyboardInterrupt, SystemExit):
raise
except:
sys.excepthook(*sys.exc_info())
self.run = run_with_except_hook
threading.Thread.__init__ = init
例外ログのテストを開始するまで、私はそれがすべて間違っていることに気付きました。
テストするために私は
raise Exception("Test")
私のコードのどこかに。ただし、このメソッドを呼び出したメソッドをラップすることは、トレースバックを出力して例外を飲み込んだブロックを除いた試行でした。これは、トレースバックブリングがSTDOUTに出力されるのに記録されないので、非常にイライラしました。トレースバックを記録するはるかに簡単な方法は、すべてのpythonコードがトレースバック自体を出力するために使用するメソッドtraceback.print_exceptionにパッチを当てることだけであると判断しました。私は次のようなものになりました:
def add_custom_print_exception():
old_print_exception = traceback.print_exception
def custom_print_exception(etype, value, tb, limit=None, file=None):
tb_output = StringIO.StringIO()
traceback.print_tb(tb, limit, tb_output)
logger = logging.getLogger('customLogger')
logger.error(tb_output.getvalue())
tb_output.close()
old_print_exception(etype, value, tb, limit=None, file=None)
traceback.print_exception = custom_print_exception
このコードは、トレースバックを文字列バッファーに書き込み、ログ記録エラーに記録します。 ERRORレベルのログを取得し、分析のためにそれらを家に送る「customLogger」ロガーを設定したカスタムロギングハンドラーがあります。
ハンドラーを sys.excepthook
に割り当てることにより、おそらくPythonのロギング関数の exc_info
パラメーターを使用して、メインスレッドでキャッチされなかったすべての例外を記録できます :
import sys
import logging
logging.basicConfig(filename='/tmp/foobar.log')
def exception_hook(exc_type, exc_value, exc_traceback):
logging.error(
"Uncaught exception",
exc_info=(exc_type, exc_value, exc_traceback)
)
sys.excepthook = exception_hook
raise Exception('Boom')
ただし、プログラムでスレッドを使用する場合は、 threading.Thread
を使用して作成されたスレッドは、notがsys.excepthook
をトリガーすることに注意してください。 Pythonの課題追跡の Issue 1230540 に記載されているように、キャッチされない例外がそれらの内部で発生します。サルをパッチしてThread.__init__
を上書きし、オリジナルをrun
ブロックでラップし、try
ブロック内からself.run
を呼び出す別のexcept
メソッドでsys.excepthook
を上書きするなど、この制限を回避するためのハッキングがいくつか提案されています。または、各スレッドのエントリポイントを手動でtry
/except
にラップすることもできます。
キャッチされない例外メッセージはSTDERRに送られるため、Python自体でログを実装する代わりに、Pythonスクリプトを実行するために使用しているシェルを使用してSTDERRをファイルに送信できます。 Bashスクリプトでは、 BASHガイドで説明 のように、出力リダイレクトでこれを行うことができます。
エラーをファイルに追加し、その他の出力を端末に追加します。
./test.py 2>> mylog.log
インターリーブされたSTDOUTおよびSTDERR出力でファイルを上書きします。
./test.py &> mylog.log
私が探していたもの:
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
traceback_in_var = traceback.format_tb(exc_traceback)
見る:
それほどスタイリッシュではないかもしれませんが、簡単です:
#!/bin/bash
log="/var/log/yourlog"
/path/to/your/script.py 2>&1 | (while read; do echo "$REPLY" >> $log; done)