接前兩篇“運行時查看線程資訊”的部落格,我在想,既然我可以隨時列印線程資訊,那麼我是不是可以隨時列印進程內部的其它資訊呢。比如,即時查看一些對象屬性等,這樣可以協助我們在不重新啟動應用程式的情況下就可以觀察進程的執行狀態。(這裡暫時不考慮那些使用第三方庫或工具的情況)
根據這個想法,查看了一下python的動態載入模組的方法,感覺這個想法還是比較靠譜,應該可以實現,所以動手寫了個小測實驗證了一把。(這裡說明一下,只是驗證性的,生產環境要使用的話,還是有不少問題需要考慮的。)
下面就是測試時考慮要做到的
- 還是使用 SIGQUIT 訊號即“kill -3”來觸發執行列印進程內部對象屬性.
- foo.py主程式,包括註冊訊號處理函數,建立一個全域的對象用來儲存一些屬性,啟動一個線程讓主線程不退出。
- foo.py主程式中的訊號處理函數動態載入一個指定路徑的下的模組,這裡我們就假定這個模組路徑是”/tmp/my_modules”,可以根據需要修改。然後調用這個模組中的方法來列印一些進程資訊。
- /tmp/my_modules/bar.py需要動態載入的模組,其中訪問foo模組中的一個對象,並列印對象屬性。
- 要能隨時動態修改要查看的進程狀態,即在不重啟進程的情況下,通過修改bar.py檔案修改要實現查看的內容。 主程式 foo.py
#!/usr/bin/env /usr/bin/python3.4# -*- coding: utf-8 -*-import sysimport threadingimport signalfrom datetime import datetimeimport timeclass MyObject(object): def __init__(self): self.data = {} self.data['a'] = 'aaa' self.data['b'] = 'bbb' self.data['c'] = 'ccc'def test(): while True: print(datetime.now()) time.sleep(2)# 訊號處理函數def signal_handler(signum, frame): try: # 動態載入模組 sys.path.append("/tmp/my_modules") # 匯入bar模組 bar = __import__('bar') # 重新載入模組,為的是可以隨時重新載入模組 reload(bar) # 調用動態載入模組的方法 bar.execute() except BaseException as e: print(e)my_object = MyObject()if __name__ == "__main__": try: signal.signal(signal.SIGQUIT, signal_handler) threading.Thread(target=test).start() while True: time.sleep(60) except KeyboardInterrupt: sys.exit(1)
需要動態載入的模組 /tmp/my_modules/bar.py
#!/usr/bin/env /usr/bin/python3.4# -*- coding: utf-8 -*-import foodef execute(): # 列印foo模組中的對象 print "my_object: %s " % foo.my_object.data
測試
首先運行foo.py
$ python foo.py
然後找到foo.py的進程號,然後使用“kill -3”來觸發列印記憶體對象的方法
$ kill -3 <pid>
此時應該可以看到foo.py進程列印my_object的屬性。
修改一下 /tmp/my_modules/bar.py 檔案,然後再次運行“$ kill -3 ”,可以看到模組被重新載入了,然後列印的新的內容。