Pythonでキャッチされなかった例外のロギング


181

どのようにキャッチされない例外をloggingモジュールではなくモジュール経由で出力させますstderrか?

これを行う最良の方法は次のとおりです。

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)

しかし、私の状況はそれがになるようで本当にいい場合はlogging.exception(...)例外がキャッチされていないたびに自動的に呼び出されました。



回答:


143

Nedが指摘したようにsys.excepthook、例外が発生してキャッチされないたびに呼び出されます。これが実際に意味することは、コードでのデフォルトの動作をオーバーライドして、sys.excepthook(の使用を含むlogging.exception)何でもしたいことを実行できることです。

麦わらの一例として:

>>> import sys
>>> def foo(exctype, value, tb):
...     print 'My Error Information'
...     print 'Type:', exctype
...     print 'Value:', value
...     print 'Traceback:', tb
... 

オーバーライドsys.excepthook

>>> sys.excepthook = foo

明白な構文エラーをコミットし(コロンは省略)、カスタムエラー情報を取得します。

>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None

の詳細についてsys.excepthook、ドキュメントをご覧ください。


4
@Codemonkeyこれは予約済みのキーワードではなく、既存の型名です。type関数の引数として使用できますが、IDEはグローバルの非表示について不満を示しますtypevar self = thisJavascriptで使用するのとよく似ています)。type関数内のオブジェクトにアクセスする必要がなければ、実際には問題になりません。その場合はtype_、代わりに引数として使用できます。
Ryan P

3
ここの「毎回」というフレーズは誤解を招くものです。: 「例外が発生してキャッチされないたびに sys.excepthookが呼び出される ...プログラムでは、「キャッチされない」例外が1つだけ存在する可能性が あるためです。また、例外が「発生」しても呼び出されません。キャッチされない例外が原因でプログラムが終了しようとしているときに呼び出されます。sys.excepthook
Nawaz、2016年

2
@Nawaz:REPLで2回以上発生する可能性があります
jfs

2
@Nawazプログラムがスレッドを使用している場合も、複数回発生する可能性があります。私はまた、例外がsys.excepthookにそれを作っているにもかかわらず、実行し続ける(Qtのような)GUIイベントループを思えてきました
three_pineapples

1
上記のコードをテストする場合は、関数のテスト中にトレースバックエラーが発生することを確認してください。SyntaxErrorはsys.excepthookによって処理されていません。print(1/0)を使用すると、sys.excepthookをオーバーライドするために定義した関数が呼び出されます
Parth Karia

177

以下は、他のいくつかのトリックも含む完全な小さな例です。

import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")
  • KeyboardInterruptを無視して、コンソールのPythonプログラムがCtrl + Cで終了できるようにします。

  • 例外のフォーマットは、Pythonのロギングモジュールに完全に依存します。

  • サンプルハンドラーでカスタムロガーを使用します。これは、未処理の例外をstderrではなくstdoutに移動するように変更しますが、この同じスタイルのすべての種類のハンドラーをロガーオブジェクトに追加できます。


13
logger.critical()キャッチされていない例外はかなり重要であるため、excepthookハンドラー内で使用します。
gitaarik 2015年

2
これがIMOの最も実用的な答えです。
デビッドモラレス

@gnu_lorienスニペットをありがとう。これをどのファイルに入れますか?
stelios 2017

@chefarov他のすべてのロギングを初期化するメインファイル
gnu_lorien 2017

こんにちは、この情報をdebug.logのようなファイルに書き留めるにはどうすればよいですか。行を追加してみますが、logging.basicConfig(level=logging.DEBUG, filename="debug.log", format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')助けにはなりませんでした。
GurhanCagin 2018

26

このメソッドsys.excepthookは、例外がキャッチされない場合に呼び出されます。http//docs.python.org/library/sys.html#sys.excepthook

例外が発生してキャッチされない場合、インタープリターはsys.excepthookを3つの引数(例外クラス、例外インスタンス、トレースバックオブジェクト)で呼び出します。インタラクティブセッションでは、これは制御がプロンプトに戻る直前に発生します。Pythonプログラムでは、これはプログラムが終了する直前に発生します。このようなトップレベルの例外の処理は、sys.excepthookに別の3つの引数の関数を割り当てることによってカスタマイズできます。


2
なぜ例外クラスを送信するのですか?typeインスタンスを呼び出すことで常にそれを取得できないのですか?
Neil G

のパラメータのタイプは何sys.excepthookですか?
Martin Thoma

23

何故なの:

import sys
import logging
import traceback

def log_except_hook(*exc_info):
    text = "".join(traceback.format_exception(*exc_info))
    logging.error("Unhandled exception: %s", text)

sys.excepthook = log_except_hook

None()

sys.excepthook上記の出力は次のとおりです。

$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

これはsys.excepthookコメントアウトされた出力です:

$ python tb.py
Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

唯一の違いは、前者がERROR:root:Unhandled exception:最初の行の先頭にあることです。


もう1つの違いは、前者はトレースをロギングシステムに書き込むため、インストールしたハンドラーとフォーマッターが適用されることです。後者は直接に書き込みますsys.stderr
radiaph

8

Jacindaの答えを基にして、ロガーオブジェクトを使用するには:

def catchException(logger, typ, value, traceback):
    logger.critical("My Error Information")
    logger.critical("Type: %s" % typ)
    logger.critical("Value: %s" % value)
    logger.critical("Traceback: %s" % traceback)

# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func

2
functools.partial()ラムダの代わりに使用する方が良いでしょう。参照: docs.python.org/2/library/functools.html#functools.partial
Mariusz Jamro

@MariuszJamroなんで?
davr

4

アプリエントリの呼び出しをtry...exceptブロックでラップして、キャッチされなかったすべての例外をキャッチしてログに記録する(そしておそらく再発生させる)ことができます。例えば:の代わりに:

if __name__ == '__main__':
    main()

これを行う:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise

これは質問の対象ではありません。質問の意図は、例外がコードによって処理されない場合にどうするかを尋ねることです。
Mayank Jaiswal 2016年

1
さて、Pythonはプログラミング言語であり、これは、OPが要求する場合とそうでない場合を除いて、(OPが望むように)自動的に実行しないことを意味します。言い換えれば、コード化しない限り、すべての例外を「自動的に」ログに記録する方法はありません。それが私の答えです。
flaviovs 2016年

1
Ned Batchelderの答えを見ると、例外フックと呼ばれるものがあります。コード内の1つの場所で定義する必要があり、キャッチされなかったすべての例外が処理されます。
Mayank Jaiswal 2016年

1
例外フックは、(OPが望む意味で)「自動」ではないという事実を変更しません-言い換えれば、まだコーディングする必要があります。ネッドの答え(例外フックを使用)は本当に元の質問に対応しています- 私の意見では、それはのやり方よりもpythonicのほうがはるかに少ないということです。
flaviovs 2016年

1
それはあなた自身の目的にかなり依存します。IDEを満足させるようにプログラムする場合、すべての例外をキャッチすることはオプションではない可能性があります。ただし、エラーを適切に処理し、ユーザーに適切なフィードバックを表示したい場合は、すべての例外をキャッチする必要があると思います。さて、皮肉は十分です:-)-注意深く見ると、コードが例外をインターセプトしているのがわかりますが、再度レイズするため、IDEが「不思議な」ことをしていない限り、それは実行されません。例外。
flaviovs 2017

3

多分あなたはstderrをファイルにリダイレクトするモジュールの上部で何かをして、そのファイルを下部に記録することができます

sock = open('error.log', 'w')               
sys.stderr = sock

doSomething() #makes errors and they will log to error.log

logging.exception(open('error.log', 'r').read() )

3

@gnu_lorienの答えは私に良い出発点を与えましたが、私のプログラムは最初の例外でクラッシュします。

私はカスタマイズされた(および/または)改良されたソリューションを用意しました@handle_error

import logging

__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)


def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))


def handle_error(func):
    import sys

    def __inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_tb = sys.exc_info()
            handle_exception(exc_type, exc_value, exc_tb)
        finally:
            print(e.message)
    return __inner


@handle_error
def main():
    raise RuntimeError("RuntimeError")


if __name__ == "__main__":
    for _ in xrange(1, 20):
        main()

2

承認された回答のコメントセクションで説明されているZeus氏からの質問に答えるために、これを使用して、キャッチされなかった例外をインタラクティブコンソールに記録します(PyCharm 2018-2019でテスト済み)。私sys.excepthookはpythonシェルでは動作しないことがわかったので、より深く見て、sys.exc_info代わりに使用できることを発見しました。ただし、3つの引数を取るのsys.exc_infoとは異なりsys.excepthook、引数はありません。

ここでは、私は両方を使用sys.excepthookし、sys.exc_info対話型のコンソールの両方の例外とラッパー関数でスクリプトをログに記録します。両方の関数にフック関数をアタッチするために、引数が指定されているかどうかに応じて、2つの異なるインターフェースがあります。

これがコードです:

def log_exception(exctype, value, traceback):
    logger.error("Uncaught exception occurred!",
                 exc_info=(exctype, value, traceback))


def attach_hook(hook_func, run_func):
    def inner(*args, **kwargs):
        if not (args or kwargs):
            # This condition is for sys.exc_info
            local_args = run_func()
            hook_func(*local_args)
        else:
            # This condition is for sys.excepthook
            hook_func(*args, **kwargs)
        return run_func(*args, **kwargs)
    return inner


sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)

ロギング設定はgnu_lorienの回答にあります。


2

python 3@Jacindaの回答を使用しているときに(を使用して)私の場合、トレースバックの内容が印刷されませんでした。代わりに、オブジェクト自体を出力します<traceback object at 0x7f90299b7b90>

代わりに私はします:

import sys
import logging
import traceback

def custom_excepthook(exc_type, exc_value, exc_traceback):
    # Do not print exception when user cancels the program
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logging.error("An uncaught exception occurred:")
    logging.error("Type: %s", exc_type)
    logging.error("Value: %s", exc_value)

    if exc_traceback:
        format_exception = traceback.format_tb(exc_traceback)
        for line in format_exception:
            logging.error(repr(line))

sys.excepthook = custom_excepthook
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.