_func2
_およびfunc
関数の呼び出しを含む、次の場合に完全なトレースバックを取得するにはどうすればよいですか?
_import traceback
def func():
try:
raise Exception('Dummy')
except:
traceback.print_exc()
def func2():
func()
func2()
_
これを実行すると、次のようになります。
_Traceback (most recent call last):
File "test.py", line 5, in func
raise Exception('Dummy')
Exception: Dummy
_
サードパーティのモジュールにtraceback
オブジェクトを渡す必要があるため、traceback.format_stack()
は私が望むものではありません。
私はこのケースに特に興味があります:
_import logging
def func():
try:
raise Exception('Dummy')
except:
logging.exception("Something awful happened!")
def func2():
func()
func2()
_
この場合、私は得ています:
_ERROR:root:Something awful happened!
Traceback (most recent call last):
File "test.py", line 9, in func
raise Exception('Dummy')
Exception: Dummy
_
Mechmindが回答したように、スタックトレースは、例外が発生したサイトとtry
ブロックのサイトの間のフレームのみで構成されています。完全なスタックトレースが必要な場合は、運が悪いようです。
スタックエントリをトップレベルから現在のフレームに抽出することが明らかに可能であることを除いて、_traceback.extract_stack
_はそれをうまく管理します。問題は、_traceback.extract_stack
_によって取得される情報は、トレースバックオブジェクトを作成せずにスタックフレームを直接検査することから得られ、logging
APIはトレースバック出力に影響を与えるためにトレースバックオブジェクトを必要とすることです。
幸い、logging
にはactualトレースバックオブジェクトは必要ありません。traceback
モジュールのフォーマットルーチンに渡すことができるオブジェクトが必要です。 traceback
もどちらでもかまいません。トレースバックの2つの属性、フレームと行番号のみを使用します。したがって、ダック型の偽トレースバックオブジェクトのリンクリストを作成し、それをトレースバックとして渡すことができるはずです。
_import sys
class FauxTb(object):
def __init__(self, tb_frame, tb_lineno, tb_next):
self.tb_frame = tb_frame
self.tb_lineno = tb_lineno
self.tb_next = tb_next
def current_stack(skip=0):
try: 1/0
except ZeroDivisionError:
f = sys.exc_info()[2].tb_frame
for i in xrange(skip + 2):
f = f.f_back
lst = []
while f is not None:
lst.append((f, f.f_lineno))
f = f.f_back
return lst
def extend_traceback(tb, stack):
"""Extend traceback with stack info."""
head = tb
for tb_frame, tb_lineno in stack:
head = FauxTb(tb_frame, tb_lineno, head)
return head
def full_exc_info():
"""Like sys.exc_info, but includes the full traceback."""
t, v, tb = sys.exc_info()
full_tb = extend_traceback(tb, current_stack(1))
return t, v, full_tb
_
これらの関数を配置すると、コードに必要な変更は簡単になります。
_import logging
def func():
try:
raise Exception('Dummy')
except:
logging.error("Something awful happened!", exc_info=full_exc_info())
def func2():
func()
func2()
_
...予想される出力を与えるには:
_ERROR:root:Something awful happened!
Traceback (most recent call last):
File "a.py", line 52, in <module>
func2()
File "a.py", line 49, in func2
func()
File "a.py", line 43, in func
raise Exception('Dummy')
Exception: Dummy
_
Faux-tracebackオブジェクトは、実際のスタックフレームへの参照が含まれているため、イントロスペクション(ローカル変数の表示またはpdb.post_mortem()
への引数として)に完全に使用できることに注意してください。
スタックトレースは、例外が発生したときに収集されます。したがって、目的のスタックの上にトレースバックを出力する必要があります。
import traceback
def func():
raise Exception('Dummy')
def func2():
func()
try:
func2()
except:
traceback.print_exc()
より完全なトレースバックを書き込むモジュールを作成しました
(また、あなたはpypiからモジュールを取得することができます
Sudo pip install pd
)
例外をキャッチして鳴らすには、次のようにします。
import pd
try:
<python code>
except BaseException:
pd.print_exception_ex( follow_objects = 1 )
スタックトレースは次のようになります。
Exception: got it
#1 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 1) at t test_pd.py:29
Calls next frame at:
raise Exception('got it') at: test_pd.py:29
#2 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 2) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#3 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 3) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#4 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 4) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#5 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 5) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#6 def kuku2(self = {'a': 42, 'b': [1, 2, 3, 4]}, depth = 6) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#7 def main() at test_pd.py:44
Local variables:
n = {'a': 42, 'b': [1, 2, 3, 4]}
Calls next frame at:
pd.print_exception_ex( follow_objects = 1 ) at: test_pd.py:44
follow_objects = 0は、オブジェクトの内容を出力しません(複雑なデータ構造では、follow_objectsには時間がかかる場合があります)。
これは@ user4815162342の回答に基づいていますが、もう少しミニマルです。
_import sys
import collections
FauxTb = collections.namedtuple("FauxTb", ["tb_frame", "tb_lineno", "tb_next"])
def full_exc_info():
"""Like sys.exc_info, but includes the full traceback."""
t, v, tb = sys.exc_info()
f = sys._getframe(2)
while f is not None:
tb = FauxTb(f, f.f_lineno, tb)
f = f.f_back
return t, v, tb
_
sys._getframe()
の使用を必要とする代償として、ダミー例外のスローを回避します。スタックフレーム(_full_exc_info
_と_full_exc_info
_を呼び出す関数)になるため、例外がキャッチされたexcept
句を使用していることを前提としています。発生コードを呼び出すため、元のトレースバックに既に含まれています)。
これにより、user4815162342の回答のコードと同じ出力が得られます。
書式のわずかな違いを気にしない場合は、
_import logging
def func():
try:
raise Exception('Dummy')
except:
logging.exception("Something awful happened!", stack_info=True)
def func2():
func()
func2()
_
その結果
_ERROR:root:Something awful happened!
Traceback (most recent call last):
File "test.py", line 5, in func
raise Exception('Dummy')
Exception: Dummy
Stack (most recent call last):
File "test.py", line 12, in <module>
func2()
File "test.py", line 10, in func2
func()
File "test.py", line 7, in func
logging.exception("Something awful happened!", stack_info=True)
_
この場合、試行から例外まで、そしてルート呼び出しからロギング呼び出しの場所まで1秒でトレースを取得します。