Python與C#不同,他不會預設就將完整的CallStack都夾帶在Exception物件裡面,而是需要透過其他類別來協助才能看到這些資訊。
寫好一段程式之後,通常都是在本機端做好單元測試或是整合測試才會上線。不過,使用Python撰寫的程式在上線之後,如果發生了意想不到的例外錯誤,要怎麼將它紀錄下來呢?
先說明一下Python的Exception機制,我們建立一支.py程式叫做testException.py,裡面放一個最簡單的try-catch,用1除以0:
# -*- coding: utf-8 -*-
try:
a = 1 / 0
except Exception as e:
print(e)
得到的錯誤訊息是:
division by zero
的確就是這樣沒錯,但是如果它發生在另外一支.py檔案裡面呢?我們先定義一支新的.py程式叫做test.py,定義一個方法叫做run,再把a = 1 / 0移到方法裡面:
# -*- coding: utf-8 -*-
def run():
a = 1 / 0
原本的testException.py改成這樣:
# -*- coding: utf-8 -*-
import test
try:
test.run()
except Exception as e:
print(e)
得到的錯誤訊息還是一樣:
division by zero
這下問題來了,我要怎麼知道Exception是發生在哪一支程式的哪一行,錯誤類型以及詳細內容又是什麼???這時候就要依靠sys與traceback套件來做這些事情:
# -*- coding: utf-8 -*-
import sys
import traceback
import test
try:
test.run()
except Exception as e:
# print(e)
error_class = e.__class__.__name__ #取得錯誤類型
detail = e.args[0] #取得詳細內容
cl, exc, tb = sys.exc_info() #取得Call Stack
lastCallStack = traceback.extract_tb(tb)[-1] #取得Call Stack的最後一筆資料
fileName = lastCallStack[0] #取得發生的檔案名稱
lineNum = lastCallStack[1] #取得發生的行號
funcName = lastCallStack[2] #取得發生的函數名稱
errMsg = "File \"{}\", line {}, in {}: [{}] {}".format(fileName, lineNum, funcName, error_class, detail)
print(errMsg)
大致上的流程是先取得Exception的類型以及內容,然後取得目前的Call Stack,再利用traceback套件來解開Stack內容,就可以取得檔名、行號、函數名稱等重要資訊。
[補充]
如果需要完整的CallStack,可以直接使用這樣來取得一個完整的CallStack字串(就像是把try-catch拿掉並在Spyder裡面執行的那種格式):
traceback.format_exc()
得到這些資訊之後,就可以再搭配Python內建的logging套件來做錯誤紀錄,並且使用SMTP發信通知系統維運人員~
Python Version:
3.6.6
Python Packages:
sys
traceback
Reference:
1. https://docs.python.org/3.6/library/stdtypes.html#instance.__class__
2. https://docs.python.org/3.6/tutorial/errors.html
3. https://docs.python.org/3.6/library/traceback.html