Nhận mô tả ngoại lệ và theo dõi ngăn xếp gây ra ngoại lệ, tất cả dưới dạng một chuỗi


423

Tôi đã thấy rất nhiều bài viết về theo dõi ngăn xếp và ngoại lệ trong Python. Nhưng tôi không tìm thấy thứ tôi cần.

Tôi có một đoạn mã Python 2.7 có thể đưa ra một ngoại lệ. Tôi muốn bắt nó và gán cho một chuỗi mô tả đầy đủ của nó và dấu vết ngăn xếp gây ra lỗi (đơn giản là tất cả những gì chúng ta sử dụng để xem trên bảng điều khiển). Tôi cần chuỗi này để in nó vào một hộp văn bản trong GUI.

Một cái gì đó như thế này:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

Vấn đề là: chức năng là complete_exception_descriptiongì?

Câu trả lời:


615

Xem tracebackmô-đun, cụ thể là format_exc()chức năng. Đây .

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb

2
Điều này chỉ làm việc với lỗi cuối cùng? Điều gì xảy ra nếu bạn bắt đầu chuyển lỗi xung quanh sang các bit mã khác? Tôi đang viết một log_error(err)chức năng.
AnnanFay

Nó hoạt động với lỗi đã bị bắt và xử lý.
kindall

74

Chúng ta hãy tạo một stacktrace phức tạp, để chứng minh rằng chúng ta có được stacktrace đầy đủ:

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

def do_something_that_might_error():
    raise_error()

Ghi nhật ký stacktrace đầy đủ

Một cách thực hành tốt nhất là thiết lập một logger cho mô-đun của bạn. Nó sẽ biết tên của mô-đun và có thể thay đổi cấp độ (trong số các thuộc tính khác, chẳng hạn như trình xử lý)

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

Và chúng ta có thể sử dụng bộ ghi này để nhận lỗi:

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

Nhật ký nào:

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!

Và do đó, chúng tôi nhận được đầu ra giống như khi chúng tôi gặp lỗi:

>>> 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!

Chỉ lấy chuỗi

Nếu bạn thực sự chỉ muốn chuỗi, traceback.format_excthay vào đó hãy sử dụng hàm, chứng minh ghi nhật ký chuỗi ở đây:

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

Nhật ký nào:

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!

1
Đây có phải là phương pháp tốt nhất khi sử dụng python 3 (so với ví dụ như một số câu trả lời dưới đây)?
Yunti

1
@Yunti Tôi tin rằng API này đã nhất quán trên Python 2 và 3.
Aaron Hall

Định dạng của câu trả lời này đã được thảo luận trên meta: meta.stackoverflow.com/questions/386477/ .
Lundin

Tôi đã gửi một chỉnh sửa đến sau nhưng không được đăng nhập để hiển thị dưới dạng ẩn danh: except Exception as e: logger.exception("<<clearly and distinctly describe what failed here>>", exc_info=e)
arntg

@arntg Tôi đánh giá cao việc bạn đang cố gắng giúp đỡ, nhưng chỉnh sửa đó sẽ là một thay đổi có hại. Xin hãy cẩn thận hơn trong tương lai để hiểu đầy đủ các API bạn đang cố sử dụng. Trong trường hợp này, exc_infođối số mong đợi một "bộ ngoại lệ" trong khi đó errorlà một thể hiện của Exceptionđối tượng (hoặc lớp con) và không cần phải thay đổi errorthành e.
Aaron Hall

39

Với Python 3, đoạn mã sau sẽ định dạng một Exceptionđối tượng chính xác như sẽ thu được bằng cách sử dụng traceback.format_exc():

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

Ưu điểm là chỉ cần Exceptionđối tượng (nhờ __traceback__thuộc tính được ghi lại ) và do đó có thể dễ dàng chuyển qua làm đối số cho hàm khác để xử lý thêm.


1
Nó tốt hơn sys.exc_info () không phải là kiểu OO tốt và sử dụng biến toàn cục.
WeiC Breath 煒 清

Điều này hỏi cụ thể làm thế nào để có được truy nguyên từ đối tượng ngoại lệ như bạn đã làm ở đây: stackoverflow.com/questions/11414894/
Kẻ

Có một cách Python3 đơn giản hơn mà không cần sử dụng .__traceback__type, xem stackoverflow.com/a/58764987/5717886
don_vanchos

34
>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Bạn sử dụng sys.exc_info () để thu thập thông tin và các chức năng trong tracebackmô-đun để định dạng thông tin. Đây là một số ví dụ để định dạng nó.

Toàn bộ chuỗi ngoại lệ là tại:

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']

9

Đối với những người sử dụng Python-3

Sử dụng tracebackmô-đun và exception.__traceback__người ta có thể trích xuất theo dõi ngăn xếp như sau:

  • lấy theo dõi ngăn xếp hiện tại bằng cách sử dụngtraceback.extract_stack()
  • xóa ba phần tử cuối cùng (vì đó là các mục trong ngăn xếp đưa tôi đến chức năng gỡ lỗi của tôi)
  • nối thêm __traceback__từ đối tượng ngoại lệ bằng cách sử dụngtraceback.extract_tb()
  • định dạng toàn bộ bằng cách sử dụng traceback.format_list()
import traceback
def exception_to_string(excp):
   stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__)  # add limit=?? 
   pretty = traceback.format_list(stack)
   return ''.join(pretty) + '\n  {} {}'.format(excp.__class__,excp)

Một minh chứng đơn giản:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

Chúng tôi nhận được đầu ra sau khi chúng tôi gọi bar():

  File "./test.py", line 57, in <module>
    bar()
  File "./test.py", line 55, in bar
    return foo()
  File "./test.py", line 50, in foo
    something_invalid()

  <class 'NameError'> name 'something_invalid' is not defined

Nó trông giống như một mã phức tạp không thể đọc được. Trong Python 3.5+ có một cách đơn giản và thanh lịch hơn: stackoverflow.com/a/58764987/5717886
don_vanchos

6

Bạn cũng có thể cân nhắc sử dụng mô-đun Python tích hợp, cgitb , để có được một số thông tin ngoại lệ thực sự tốt, được định dạng độc đáo bao gồm các giá trị biến cục bộ, bối cảnh mã nguồn, tham số hàm, v.v.

Ví dụ cho mã này ...

import cgitb
cgitb.enable(format='text')

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

chúng tôi nhận được đầu ra ngoại lệ này ...

ZeroDivisionError
Python 3.4.2: C:\tools\python\python.exe
Tue Sep 22 15:29:33 2015

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 c:\TEMP\cgittest2.py in <module>()
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
func1 = <function func1>

 c:\TEMP\cgittest2.py in func1(a=1, b=5)
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0

 c:\TEMP\cgittest2.py in func2(a=1, divisor=0)
    3
    4 def func2(a, divisor):
    5   return a / divisor
    6
    7 def func1(a, b):
a = 1
divisor = 0
ZeroDivisionError: division by zero
    __cause__ = None
    __class__ = <class 'ZeroDivisionError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of ZeroDivisionError object>
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
    __format__ = <built-in method __format__ of ZeroDivisionError object>
    __ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
    __gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of ZeroDivisionError object>
    __le__ = <method-wrapper '__le__' of ZeroDivisionError object>
    __lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
    __ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ('division by zero',)
    with_traceback = <built-in method with_traceback of ZeroDivisionError object>

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgittest2.py", line 11, in <module>
    func1(1, 5)
  File "cgittest2.py", line 9, in func1
    return func2(a, c)
  File "cgittest2.py", line 5, in func2
    return a / divisor
ZeroDivisionError: division by zero

5

Nếu bạn muốn nhận được thông tin tương tự được đưa ra khi một ngoại lệ không được xử lý, bạn có thể làm điều gì đó như thế này. Làm import tracebackvà sau đó:

try:
    ...
except Exception as e:
    print(traceback.print_tb(e.__traceback__))

Tôi đang sử dụng Python 3.7.


3

Đối với Python 3.5+ :

Vì vậy, bạn có thể lấy stacktrace từ ngoại lệ của mình như từ bất kỳ ngoại lệ nào khác. Sử dụng traceback.TracebackExceptioncho nó (chỉ cần thay thế exbằng ngoại lệ của bạn):

print("".join(traceback.TracebackException.from_exception(ex).format())

Một ví dụ mở rộng và các tính năng khác để làm điều này:

import traceback

try:
    1/0
except Exception as ex:
    print("".join(traceback.TracebackException.from_exception(ex).format()) == traceback.format_exc() == "".join(traceback.format_exception(type(ex), ex, ex.__traceback__))) # This is True !!
    print("".join(traceback.TracebackException.from_exception(ex).format()))

Đầu ra sẽ giống như thế này:

True
Traceback (most recent call last):
  File "untidsfsdfsdftled.py", line 29, in <module>
    1/0
ZeroDivisionError: division by zero

1

2 xu của tôi:

import sys, traceback
try: 
  ...
except Exception, e:
  T, V, TB = sys.exc_info()
  print ''.join(traceback.format_exception(T,V,TB))

1

Nếu mục tiêu của bạn là làm cho ngoại lệ và thông báo stacktrace trông giống hệt như khi python ném lỗi, thì cách sau sẽ hoạt động trong cả python 2 + 3:

import sys, traceback


def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# EXAMPLE BELOW...

def a():
    b()


def b():
    c()


def c():
    d()


def d():
    assert False, "Noooh don't do it."


print("THIS IS THE FORMATTED STRING")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("THIS IS HOW PYTHON DOES IT")
print("==========================\n")
a()

Nó hoạt động bằng cách loại bỏ format_stacktrace()cuộc gọi cuối cùng khỏi ngăn xếp và tham gia phần còn lại. Khi chạy, ví dụ trên cho đầu ra sau:

THIS IS THE FORMATTED STRING
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

THIS IS HOW PYTHON DOES IT
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

0

Tôi đã định nghĩa lớp người trợ giúp sau:

import traceback
class TracedExeptions(object):
    def __init__(self):
        pass
    def __enter__(self):
        pass

    def __exit__(self, etype, value, tb):
      if value :
        if not hasattr(value, 'traceString'):
          value.traceString = "\n".join(traceback.format_exception(etype, value, tb))
        return False
      return True

Mà sau này tôi có thể sử dụng như thế này:

with TracedExeptions():
  #some-code-which-might-throw-any-exception

Và sau này có thể tiêu thụ nó như thế này:

def log_err(ex):
  if hasattr(ex, 'traceString'):
    print("ERROR:{}".format(ex.traceString));
  else:
    print("ERROR:{}".format(ex));

(Bối cảnh: Tôi đã thất vọng vì sử dụng Promises cùng với Exceptions, điều này không may chuyển các ngoại lệ được nêu ra ở một nơi sang một trình xử lý on numposed ở một nơi khác, và do đó rất khó để có được truy nguyên từ vị trí ban đầu)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.