Helpex - Trao đổi & giúp đỡ Đăng nhập
910

Tôi muốn nắm bắt và ghi lại các ngoại lệ mà không cần thoát, ví dụ:

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    # I want to print the entire traceback here,
    # not just the exception name and details

Tôi muốn in cùng một đầu ra chính xác được in khi ngoại lệ được nâng lên mà không cần thử..giảm trừ việc chặn ngoại lệ và tôi không muốn nó thoát khỏi chương trình của tôi. Làm thế nào để tôi làm điều này?

910 hữu ích 2 bình luận 831k xem chia sẻ
16 trả lời 16
705

Một số câu trả lời khác đã chỉ ra các traceback module.

Xin lưu ý rằng print_exc, trong một số trường hợp góc, bạn sẽ không đạt được những gì bạn mong đợi. Trong Python 2.x:

import traceback

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

    traceback.print_exc()

... sẽ hiển thị dấu vết của ngoại lệ cuối cùng :

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

Nếu bạn thực sự cần phải truy cập bản gốc traceback một giải pháp là bộ nhớ cache infos ngoại lệ như trở về từ exc_infotrong một biến địa phương và hiển thị nó bằng 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

Sản xuất:

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

Tuy nhiên, có rất ít cạm bẫy với điều này:

  • Từ tài liệu của sys_info:

    Việc gán giá trị trả về theo dõi cho một biến cục bộ trong một hàm đang xử lý một ngoại lệ sẽ gây ra một tham chiếu vòng tròn . Điều này sẽ ngăn không cho bất kỳ thứ gì được tham chiếu bởi một biến cục bộ trong cùng một hàm hoặc theo dấu vết khỏi bị thu thập rác. [...] Nếu bạn thực sự cần theo dõi lại, hãy nhớ xóa nó sau khi sử dụng (tốt nhất nên thực hiện bằng câu lệnh try ... last)

  • nhưng, từ cùng một tài liệu:

    Bắt đầu với Python 2.2, các chu trình như vậy sẽ tự động được lấy lại khi kích hoạt tính năng thu gom rác và chúng trở nên không thể truy cập được, nhưng nó vẫn hiệu quả hơn để tránh tạo các chu trình.


Mặt khác, bằng cách cho phép bạn truy cập truy xuất liên quan đến một ngoại lệ, Python 3 tạo ra một kết quả ít ngạc nhiên hơn:

import traceback

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

    traceback.print_tb(err.__traceback__)

... sẽ hiển thị:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
705 hữu ích 0 bình luận chia sẻ
789

traceback.format_exc()hoặc sys.exc_info()sẽ cung cấp thêm thông tin nếu đó là những gì bạn muốn.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[2])
789 hữu ích 4 bình luận chia sẻ
294

Nếu bạn đang gỡ lỗi và chỉ muốn xem dấu vết ngăn xếp hiện tại, bạn có thể chỉ cần gọi:

traceback.print_stack()

Không cần phải nâng một ngoại lệ theo cách thủ công chỉ để bắt lại nó.

294 hữu ích 3 bình luận chia sẻ
143

Làm thế nào để in toàn bộ theo dõi mà không phải tạm dừng chương trình?

Khi bạn không muốn tạm dừng chương trình của mình do lỗi, bạn cần xử lý lỗi đó bằng cách thử / ngoại trừ:

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

Để trích xuất toàn bộ theo dõi, chúng tôi sẽ sử dụng tracebackmô-đun từ thư viện chuẩn:

import traceback

Và để 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()

In ấn

Để in bản theo dõi đầy đủ, hãy sử dụng traceback.print_excphương pháp:

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

Những bản in:

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!

Tốt hơn so với in ấn, ghi nhật ký:

Tuy nhiên, cách tốt nhất là thiết lập một trình ghi nhật ký 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__)

Trong trường hợp đó, bạn sẽ muốn logger.exceptionhàm thay thế:

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!

Hoặc có lẽ bạn chỉ muốn chuỗi, trong trường hợp đó, bạn sẽ muốn traceback.format_exchàm thay thế:

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

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!

Phần kết luận

Và đối với cả ba tùy chọn, chúng tôi thấy 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!

Sử dụng cái nào

Các mối quan tâm về hiệu suất không quan trọng ở đây vì IO thường chiếm ưu thế. Tôi thích hơn, vì nó thực hiện chính xác những gì đang được yêu cầu theo cách tương thích về phía trước:

logger.exception(error)

Có thể điều chỉnh mức ghi và đầu ra, giúp bạn dễ dàng tắt mà không cần chạm vào mã. Và thông thường làm những gì trực tiếp cần thiết là cách hiệu quả nhất để làm điều đó.

143 hữu ích 5 bình luận chia sẻ
24

Thứ nhất, không sử dụng prints cho khai thác gỗ, có astable, đã được chứng minh và được nghĩ ra mô-đun stdlib để làm điều đó: logging. Bạn chắc chắn nên sử dụng nó để thay thế.

Thứ hai, đừng bị cám dỗ để làm lộn xộn với các công cụ không liên quan khi có cách tiếp cận bản địa và đơn giản. Nó đây:

log = logging.getLogger(__name__)

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

Đó là nó. Bạn đã hoàn thành ngay bây giờ.

Giải thích cho bất kỳ ai quan tâm đến cách mọi thứ hoạt động dưới mui xe

Những gì log.exceptionthực sự đang làm chỉ là một cuộc gọi đến log.error(nghĩa là, ghi lại sự kiện với mức độ ERROR) in theo dõi lại sau đó.

Tại sao nó tốt hơn?

Vâng, đây là một số cân nhắc:

  • nó vừa phải ;
  • nó là đơn giản;
  • nó đơn giản.

Tại sao không ai nên sử dụng tracebackhoặc trình ghi nhật ký cuộc gọi với exc_info=Truehoặc làm bẩn tay họ sys.exc_info?

Chà, chỉ vì! Tất cả chúng đều tồn tại cho những mục đích khác nhau. Ví dụ: traceback.print_excđầu ra của hơi khác một chút so với bản truy xuất do chính trình thông dịch tạo ra. Nếu bạn sử dụng nó, bạn sẽ nhầm lẫn với bất kỳ ai đọc nhật ký của bạn, họ sẽ đập đầu vào họ.

Chuyển exc_info=Trueđể ghi nhật ký cuộc gọi chỉ là không thích hợp. Tuy nhiên , nó rất hữu ích khi phát hiện các lỗi có thể khôi phục và bạn cũng muốn ghi lại chúng (bằng cách sử dụng, ví dụ: INFOcấp) bằng log.exceptioncác bản ghi theo dõi , vì chỉ tạo ra nhật ký của một cấp - ERROR.

Và bạn chắc chắn nên tránh gây rối sys.exc_infohết mức có thể. Nó không chỉ là một giao diện công khai mà là một giao diện nội bộ - bạn có thể sử dụng nó nếu bạn chắc chắn biết mình đang làm gì. Nó không nhằm mục đích chỉ in các trường hợp ngoại lệ.

24 hữu ích 3 bình luận chia sẻ
16

Ngoài câu trả lời của Aaron Hall , nếu bạn đang ghi nhật ký nhưng không muốn sử dụng logging.exception()(vì nó ghi ở cấp độ LỖI), bạn có thể sử dụng cấp độ thấp hơn và vượt qua exc_info=True. ví dụ

try:
    do_something_that_might_error()
except Exception:
    logging.info('General exception noted.', exc_info=True)
16 hữu ích 0 bình luận chia sẻ
14

Tôi không thấy điều này được đề cập trong bất kỳ câu trả lời nào khác. Nếu bạn đang truyền xung quanh một đối tượng Ngoại lệ vì bất kỳ lý do gì ...

Trong Python 3.5+, bạn có thể lấy dấu vết từ một đối tượng Exception bằng cách sử dụng traceback.TracebackException.from_exception () . Ví dụ:

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()))

Tuy nhiên, đoạn mã trên dẫn đến:

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')

Đây chỉ là hai cấp của ngăn xếp, trái ngược với những gì sẽ được in trên màn hình nếu ngoại lệ được nâng lên stack_lvl_2()và không bị chặn (bỏ ghi chú # raisedòng).

Theo tôi hiểu, đó là bởi vì một ngoại lệ chỉ ghi lại mức hiện tại của ngăn xếp khi nó được nâng lên, stack_lvl_3()trong trường hợp này. Khi nó được sao lưu qua ngăn xếp, nhiều cấp độ hơn sẽ được thêm vào __traceback__. Nhưng chúng tôi đã chặn nó ở lại stack_lvl_2(), có nghĩa là tất cả những gì nó phải ghi là cấp 3 và 2. Để có được dấu vết đầy đủ như được in trên stdout, chúng tôi phải bắt nó ở cấp cao nhất (thấp nhất?):

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()))

Kết quả là:

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')

Chú ý rằng bản in chồng khác nhau, dòng đầu tiên và dòng cuối cùng bị thiếu. Bởi vì nó khác biệtformat() .

Việc chặn ngoại lệ càng xa điểm mà nó được nêu ra càng tốt sẽ làm cho mã đơn giản hơn đồng thời cung cấp nhiều thông tin hơn.

14 hữu ích 1 bình luận chia sẻ
8

Để lấy dấu vết ngăn xếp chính xác , dưới dạng một chuỗi, sẽ được nâng lên nếu không có thử / ngoại trừ ở đó để bước qua nó, chỉ cần đặt dấu vết này vào khối ngoại trừ bắt ngoại lệ vi phạm.

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

Đây là cách sử dụng nó (giả sử flaky_funcđược xác định và loggọi hệ thống ghi nhật ký yêu thích của bạn):

import traceback
import sys

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

Bạn nên bắt và tăng lại KeyboardInterruptcác s để bạn vẫn có thể ngắt chương trình bằng Ctrl-C. Ghi nhật ký nằm ngoài phạm vi của câu hỏi, nhưng một lựa chọn tốt là ghi nhật ký . Tài liệu dành cho systraceback mô-đun.

8 hữu ích 3 bình luận chia sẻ
7

Bạn sẽ cần phải thử / ngoại trừ bên trong vòng trong nhất mà lỗi có thể xảy ra, tức là

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

... và như thế

Nói cách khác, bạn sẽ cần gói các câu lệnh có thể không thành công trong lần thử / ngoại trừ càng cụ thể càng tốt, trong vòng lặp bên trong nhất có thể.

7 hữu ích 0 bình luận chia sẻ
7

Một nhận xét về nhận xét của câu trả lời này : print(traceback.format_exc())làm một công việc tốt hơn cho tôi hơn traceback.print_exc(). Với thứ hai, hellođôi khi bị "trộn lẫn" một cách kỳ lạ với văn bản theo dõi, giống như nếu cả hai muốn viết vào stdout hoặc stderr cùng một lúc, tạo ra kết quả kỳ lạ (ít nhất là khi xây dựng từ bên trong trình soạn thảo văn bản và xem kết quả đầu ra trong Bảng "Xây dựng kết quả").

Traceback (lần gọi gần đây nhất):
Tệp "C: \ Users \ User \ Desktop \ test.py", dòng 7, trong
địa ngục do_stuff ()
Tệp "C: \ Users \ User \ Desktop \ test.py", dòng 4 , trong do_stuff
1/0
ZeroDivisionError: chia số nguyên hoặc modulo bằng 0
o
[Hoàn thành trong 0,1 giây ]

Vì vậy, tôi sử dụng:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
7 hữu ích 0 bình luận chia sẻ
7

traceback.format_exception

Nếu bạn chỉ có đối tượng ngoại lệ, bạn có thể lấy truy nguyên dưới dạng chuỗi từ bất kỳ điểm nào của mã trong Python 3 với:

import traceback

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

Ví dụ đầy đủ:

#!/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)

Đầu ra:

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

Tài liệu: https://docs.python.org/3.7/library/traceback.html#traceback.format_exception

Xem thêm: Trích xuất thông tin theo dõi từ một đối tượng ngoại lệ

Đã thử nghiệm bằng Python 3.7.3.

7 hữu ích 0 bình luận chia sẻ
4

Nếu bạn đã có đối tượng Lỗi và bạn muốn in toàn bộ nội dung, bạn cần thực hiện lệnh gọi hơi khó xử này:

import traceback
traceback.print_exception(type(err), err, err.__traceback__)

Đúng vậy, print_exceptionba đối số vị trí: Loại ngoại lệ, đối tượng ngoại lệ thực tế và thuộc tính truy vết nội bộ của ngoại lệ.

Trong python 3.5 trở lên, type(err)là tùy chọn ... nhưng đó là đối số vị trí, vì vậy bạn vẫn phải chuyển rõ ràng Không có vào vị trí của nó.

traceback.print_exception(None, err, err.__traceback__)

Tôi không biết tại sao tất cả những điều này không chỉ traceback.print_exception(err). Tại sao bạn lại muốn in ra một lỗi, cùng với một dấu vết khác với lỗi thuộc về lỗi đó, nằm ngoài tôi.

4 hữu ích 0 bình luận chia sẻ
3

Bạn muốn traceback module. Nó sẽ cho phép bạn in các kết xuất ngăn xếp như Python thường làm. Đặc biệt, hàm print_last sẽ in ngoại lệ cuối cùng và một dấu vết ngăn xếp.

3 hữu ích 0 bình luận chia sẻ
0

Đây là giải pháp của tôi để ghi lỗi trong tệp nhật ký và cả trên bảng điều khiển:

import logging, sys
import traceback
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
    exc_info=(exc_type, exc_value, exc_traceback)
    logging.critical("\nDate:" + str(datetime.datetime.now()), exc_info=(exc_type, exc_value, exc_traceback))
    print("An error occured, check error.log to see the error details")
    traceback.print_exception(*exc_info)


sys.excepthook = handle_exception
0 hữu ích 0 bình luận chia sẻ
0

Bạn có thể làm:

try:
    do_stuff()
except Exception, err:
    print(Exception, err)
    raise err
0 hữu ích 0 bình luận chia sẻ
0

giải pháp python 3

stacktrace_helper.py:

from linecache import getline
import sys
import traceback


def get_stack_trace():
    exc_type, exc_value, exc_tb = sys.exc_info()
    trace = traceback.format_stack()
    trace = list(filter(lambda x: ("\\lib\\" not in x and "/lib/" not in x and "stacktrace_helper.py" not in x), trace))
    ex_type = exc_type.__name__
    ex_line = exc_tb.tb_lineno
    ex_file = exc_tb.tb_frame.f_code.co_filename
    ex_message = str(exc_value)
    line_code = ""
    try:
        line_code = getline(ex_file, ex_line).strip()
    except:
        pass

    trace.insert(
        0, f'File "{ex_file}", line {ex_line}, line_code: {line_code} , ex: {ex_type} {ex_message}',
    )
    return trace


def get_stack_trace_str(msg: str = ""):
    trace = list(get_stack_trace())
    trace_str = "\n".join(list(map(str, trace)))
    trace_str = msg + "\n" + trace_str
    return trace_str
0 hữu ích 0 bình luận chia sẻ
loading
Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ python exception try-catch traceback , hoặc hỏi câu hỏi của bạn.

Có thể bạn quan tâm

loading