web-dev-qa-db-ja.com

プログラムを停止せずにフルトレースバックを表示する方法

私は10個のウェブサイトを解析し、データファイルを見つけ、ファイルを保存し、そしてそれらを解析してNumPyライブラリですぐに使えるデータを作るプログラムを書いています。このファイルが不適切なリンク、不適切な形式のXML、入力されていないエントリ、およびその他に分類していないものを介して発生するエラーには、 トン があります。私は最初このようなエラーを処理するためにこのプログラムを作りました:

try:
    do_stuff()
except:
    pass

しかし今、私はエラーをログに記録したいです。

try:
    do_stuff()
except Exception, err:
    print Exception, err

これは後で確認するためにログファイルに出力されることに注意してください。これは通常非常に無駄なデータを出力します。 try-exceptが例外をインターセプトせずにエラーが発生したときに出力されたものとまったく同じ行を印刷することを望みますが、プログラムがforループにネストされているため、プログラムを停止させたくありません。完成まで見てください。

594
chriscauley

他の回答から、 traceback モジュールがすでに指摘されています。

print_excを使用すると、場合によっては期待通りの結果が得られないことがあります。 Python 2.xでは:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

...例外lastのトレースバックを表示します。

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

元のtracebackにアクセスする必要がある場合は、 exc_info から返されるexception infosをローカル変数にキャッシュして表示することが解決策の1つです print_exception を使う:

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

プロデュース:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

ただし、これに関する落とし穴はほとんどありません。

  • sys_info のドキュメントより:

    例外を処理している関数内のローカル変数にトレースバックの戻り値を代入すると、 循環参照 が発生します。これにより、同じ関数内のローカル変数またはトレースバックによって参照されるものがガベージコレクトされるのを防ぐことができます。 [...] トレースバックが必要な場合は、使用後に必ず削除してください (try ... finallyステートメントを使用するのが最善です)

  • しかし、同じ文書から:

    Python 2.2以降、ガベージコレクションが有効になって到達不能になった場合、このようなサイクルは自動的に再利用されます 。ただし、サイクルの作成を回避する方が効率的です。


一方、トレースバックに関連付けられているにアクセスできるようにすることで、Python 3はそれほど驚くべき結果を生み出しません。

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

...が表示されます。

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
421
Sylvain Leroux

traceback.format_exc() または sys.exc_info() は、それがあなたが望むものであればより多くの情報を生み出すでしょう。

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[0])
608
volting

デバッグ中で、現在のスタックトレースを見たいだけの場合は、単に次のように呼び出します。

traceback.print_stack()

再度キャッチするためだけに手動で例外を発生させる必要はありません。

194
dimo414

プログラムを停止せずにフルトレースバックを表示する方法

エラーでプログラムを停止したくない場合は、try/exceptでそのエラーを処理する必要があります。

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

フルトレースバックを抽出するには、標準ライブラリのtracebackモジュールを使います。

import traceback

そして、完全なスタックトレースを取得することを実証するために、きわめて複雑なスタックトレースを作成するには、次のようにします。

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

印刷

フルトレースバックを print にするには、traceback.print_excメソッドを使用します。

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

どの印刷物:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

印刷、ロギングよりも優れています。

ただし、ベストプラクティスは、ロガーを自分のモジュール用に設定することです。それはモジュールの名前を知っていて、レベルを変えることができるでしょう(他の属性の中でも、例えばハンドラーのように)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

その場合は、代わりにlogger.exception関数を使用します。

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

どのログ:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

あるいは、単に文字列が欲しいだけの場合は、代わりにtraceback.format_exc関数が必要です。

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

どのログ:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

結論

そして、3つのオプションすべてについて、エラーが発生したときと同じ出力が得られます。

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
74
Aaron Hall

最初に、ロギングにprintsを使用しないでください。それを行うための安定した、実績のある、よく考えられたstdlibモジュールがあります: logging 。間違いなくshould代わりに使用してください。

第二に、ネイティブで単純なアプローチがある場合、無関係なツールでmessを実行しようとしないでください。ここにあります:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

それでおしまい。これで完了です。

内部で物事がどのように機能するかに興味がある人への説明

log.exceptionが実際に行っているのは、単にlog.errorの呼び出しです(つまり、レベルERRORのイベントをログに記録します)andトレースバックを出力しますその後。

なぜそれが良いのですか?

さて、ここにいくつかの考慮事項があります:

  • それはただright;
  • それは簡単です。
  • 簡単です。

誰もtracebackを使用したり、exc_info=Trueでロガーを呼び出したり、sys.exc_infoで手を汚したりしないのはなぜですか?

まあ、という理由だけで!それらはすべて異なる目的のために存在します。たとえば、traceback.print_excの出力は、インタープリター自体が生成するトレースバックとは少し異なります。あなたがそれを使用する場合、あなたはあなたのログを読む人を混乱させるでしょう、彼らは彼らに対して彼らの頭を叩きます。

exc_info=Trueを渡して呼び出しをログに記録するのは不適切です。 しかし、回復可能なエラーをキャッチし、トレースバックを使用して(たとえばINFOレベルを使用して)ログを記録したい場合にも便利です、なぜならlog.exceptionは、1つのレベルのログのみを生成します-ERROR

そして、sys.exc_infoをいじることはできる限り避けるべきです。それは単なるパブリックインターフェイスではなく、内部インターフェイスです-あなたが何をしているかを明確に知っているなら、あなたはcanそれを使用します。例外を印刷するだけではありません。

13
tosh

@Aaron Hallの回答に加えて、ログに記録していてもlogging.exception()を使用したくない場合(ERRORレベルでログを記録するため)、低いレベルを使用してexc_info=Trueを渡すことができます。例えば.

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)
7
Mark McDonald

エラーが発生する可能性がある最も内側のループの内側にtry/exceptを配置する必要があります。

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

... 等々

言い換えれば、try /で失敗する可能性のあるステートメントを、可能な限り特定のものを除いて、できるだけ内側のループでラップする必要があります。

6

preciseスタックトレースを文字列として取得するには、それをまたぐためのtry/exceptがない場合はwouldが発生しています。違反している例外.

desired_trace = traceback.format_exc(sys.exc_info())

これを使用する方法は次のとおりです(flaky_funcが定義されていて、logがお気に入りのロギングシステムを呼び出すと仮定します)。

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

Ctrl-Cを使用してプログラムを強制終了できるように、KeyboardInterruptsをキャッチして再生成することをお勧めします。ロギングは質問の範囲外ですが、良い選択肢は logging です。 sys および traceback モジュールの資料。

6
Edward Newell

この答えについてのコメント のコメント:print(traceback.format_exc())は私にとってtraceback.print_exc()よりも良い仕事をしています。後者では、helloがトレースバックテキストと奇妙に "混在する"ことがあります。両方とも同時にstdoutまたはstderrに書き込みたい場合、(少なくともテキストエディタの内部からビルドしてその出力を[ビルド結果]パネル).

トレースバック(最新のコールが最後)
ファイル "C:\ Users\User\Desktop\test.py"、7行目
地獄 do_stuff()
do_stuff内のファイル "C:\ Users\User\Desktop\test.py"、4行目
1/0
ZeroDivisionError:整数除算またはゼロによるモジュロ
o
[0.1秒で終了]

だから私は使用します:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
3
Basj

traceback モジュールが必要です。それはあなたがPythonが通常するようにあなたにスタックダンプを印刷させるでしょう。特に、 print_last 関数は最後の例外とスタックトレースを表示します。

3
nmichaels

私はこれが他の答えのどれにも言及されているのを見ません。なんらかの理由でExceptionオブジェクトを渡しているなら...

Python 3.5以降では、 traceback.TracebackException.from_exception()を使用して、Exceptionオブジェクトからトレースを取得できます。例えば:

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    try:
        stack_lvl_3()
    except Exception as e:
        # raise
        return e


def stack_lvl_1():
    e = stack_lvl_2()
    return e

e = stack_lvl_1()

tb1 = traceback.TracebackException.from_exception(e)
print(''.join(tb1.format()))

ただし、上記のコードは次のようになります。

Traceback (most recent call last):
  File "exc.py", line 10, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')
Exception: ('a1', 'b2', 'c3')

これは、stack_lvl_2()で例外が発生し、傍受されなかった場合に画面に表示されたものとは対照的に、スタックの2レベルにすぎません(# raise行のコメントを外します)。

私が理解しているように、それは例外が発生した時にスタックの現在のレベル、この場合はstack_lvl_3()のみを記録するからです。それがスタックを介して渡されるにつれて、その__traceback__にもっと多くのレベルが追加されています。しかし、それをstack_lvl_2()で傍受しました。つまり、記録されるのはレベル3と2だけでした。標準出力に表示される完全なトレースを取得するには、最高(最低)レベルで捕捉する必要があります。

import traceback


def stack_lvl_3():
    raise Exception('a1', 'b2', 'c3')


def stack_lvl_2():
    stack_lvl_3()


def stack_lvl_1():
    stack_lvl_2()


try:
    stack_lvl_1()
except Exception as exc:
    tb = traceback.TracebackException.from_exception(exc)

print('Handled at stack lvl 0')
print(''.join(tb.stack.format()))

結果は次のとおりです。

Handled at stack lvl 0
  File "exc.py", line 17, in <module>
    stack_lvl_1()
  File "exc.py", line 13, in stack_lvl_1
    stack_lvl_2()
  File "exc.py", line 9, in stack_lvl_2
    stack_lvl_3()
  File "exc.py", line 5, in stack_lvl_3
    raise Exception('a1', 'b2', 'c3')

スタック印刷が異なり、最初と最後の行がなくなっていることに注意してください。それは 違うformat() だからです。

可能な限り例外が発生した場所から遠く離れて例外をインターセプトすると、より単純なコードになり、同時により多くの情報が得られます。

0
bgdnlp

traceback.format_exceptionを使用して例外オブジェクトからフルトレースバックを文字列として取得する

例外オブジェクトしかない場合は、Python 3のコードの任意の場所からトレースバックを文字列として取得できます。

import traceback

''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))

完全な例:

#!/usr/bin/env python3

import traceback

def f():
    g()

def g():
    raise Exception('asdf')

try:
    g()
except Exception as e:
    exc = e

tb_str = ''.join(traceback.format_exception(None, exc_obj, exc_obj.__traceback__))
print(tb_str)

出力:

Traceback (most recent call last):
  File "./main.py", line 12, in <module>
    g()
  File "./main.py", line 9, in g
    raise Exception('asdf')
Exception: asdf

ドキュメント: https://docs.python.org/3.7/library/traceback.html#traceback.format_exception

例外オブジェクトからのトレースバック情報の抽出

Python 3.7.3でテスト済み。

1つのライナー:

import traceback
traceback.print_exc()

traceback モジュールを使用します。これは、except:内にある場合に現在の例外を自動的に取得します。

https://stackoverflow.com/a/9555145/2217801