30

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.format_stack()tracebackオブジェクトをサードパーティのモジュールに渡す必要があるため、これは私が望むものではありません。

私はこの場合に特に興味があります:

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
4

5 に答える 5

38

mechmindが答えたように、スタックトレースは、例外が発生したサイトとtryブロックのサイトの間のフレームのみで構成されます。完全なスタックトレースが必要な場合は、どうやら運が悪いようです。

トップレベルから現在のフレームにスタックエントリを抽出することが明らかに可能であることを除いて、<code>traceback.extract_stackはそれをうまく管理します。問題は、によって取得される情報がtraceback.extract_stack、どの時点でもトレースバックオブジェクトを作成せずにスタックフレームを直接検査することから得られることであり、loggingAPIはトレースバック出力に影響を与えるためにトレースバックオブジェクトを必要とします。

幸い、実際のloggingトレースバックオブジェクトは必要ありません。モジュールのフォーマットルーチンに渡すことができるオブジェクトが必要です。どちらも気にしません—トレースバックの2つの属性、フレームと行番号のみを使用します。したがって、ダックタイプのフェイクトレースバックオブジェクトのリンクリストを作成し、それをトレースバックとして渡すことができるはずです。tracebacktraceback

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()、実際のスタックフレームへの参照を含んでいるため、イントロスペクション(ローカル変数の表示、またはへの引数として)に完全に使用できることに注意してください。

于 2012-11-03T15:22:53.723 に答える
3

例外が発生すると、スタックトレースが収集されます。したがって、目的のスタックの上にトレースバックを印刷する必要があります。

import traceback

def func():
    raise Exception('Dummy')

def func2():
    func()


try:
    func2()
except:
    traceback.print_exc()
于 2012-11-03T15:24:30.273 に答える
3

これは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()exceptこれは、例外がキャッチされた句で使用されていることを前提としています。これは、2つのスタックフレーム(full_exc_infoおよび呼び出す関数full_exc_info-発生コードを呼び出す関数であり、元のトレースバックにすでに含まれている関数)を上げるためです。

これにより、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番目のトレースを取得します。

于 2019-09-25T20:03:17.697 に答える
2

より完全なトレースバックを書き込むモジュールを作成しました

モジュールはここにありますドキュメントはドキュメントです

(また、pypiからモジュールを取得できます

sudo pip install pd

)。

例外をキャッチしてpringするには、次のようにします。

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には多くの時間がかかる可能性があります)。

于 2015-02-24T18:59:42.717 に答える
-1

トレースバックから抽出できる情報は他にもいくつかあります。トレースバックによって提供されるファイル、行番号、コードスニペットを含む複数行のブロブではなく、よりきちんとした、より「論理的な」情報を好む場合があります。できれば、1行ですべての要点を説明する必要があります。

これを実現するために、次の関数を使用します。

def raising_code_info():
    code_info = ''
    try:    
        frames = inspect.trace()
        if(len(frames)):
            full_method_name = frames[0][4][0].rstrip('\n\r').strip()
            line_number      = frames[1][2]
            module_name      = frames[0][0].f_globals['__name__']
            if(module_name == '__main__'):
                module_name = os.path.basename(sys.argv[0]).replace('.py','')
            class_name = ''
            obj_name_dot_method = full_method_name.split('.', 1)
            if len(obj_name_dot_method) > 1:
                obj_name, full_method_name = obj_name_dot_method
                try:
                    class_name = frames[0][0].f_locals[obj_name].__class__.__name__
                except:
                    pass
            method_name = module_name + '.'
            if len(class_name) > 0:
                method_name += class_name + '.'
            method_name += full_method_name
            code_info = '%s, line %d' % (method_name, line_number)
    finally:
        del frames
        sys.exc_clear()
    return code_info

それは与えます 。および行番号、例:

(モジュール名の例:test.py):

(line 73:)
def function1():
    print 1/0

class AClass(object):    
    def method2(self):
        a = []
        a[3] = 1

def try_it_out():
    # try it with a function
    try:
        function1()
    except Exception, what:
        print '%s: \"%s\"' % (raising_code_info(), what)

    # try it with a method
    try:
        my_obj_name = AClass()
        my_obj_name.method2()       
    except Exception, what:
        print '%s: \"%s\"' % (raising_code_info(), what)

if __name__ == '__main__':
     try_it_out()


test.function1(), line 75: "integer division or modulo by zero"
test.AClass.method2(), line 80: "list assignment index out of range"

これは、いくつかのユースケースでは少しすっきりしているかもしれません。

于 2013-06-09T15:15:22.860 に答える