Appium+python自動化(三十一)- 元芳,你怎麼看? - 日誌收集-logging(超詳解)
簡介
生活中的日誌是記錄你生活的點點滴滴,讓它把你內心的世界表露出來,更好的詮釋自己的內心世界,而電腦裡的日誌是有價值的資訊寶庫。
日誌檔案是專門用於記錄系統操作事件的記錄檔案或檔案集合,作業系統有作業系統日誌檔案,資料庫系統有資料庫系統日誌檔案,等等。
系統日誌檔案是包含關於系統訊息的檔案,包括核心、服務、在系統上執行的應用程式等。不同的日誌檔案記載不同的資訊。例如,有的是預設的系統日誌檔案,有的記載特定任務。
在資料庫中用事務日誌檔案記錄資料的修改操作,其中的每條日誌記錄或者記錄所執行的邏輯操作,或者記錄已修改資料的前像和後像。前像是操作執行前的資料複本;後像是操作執行後的資料複本。
問題思考
在自動化指令碼執行過程中,IDE控制檯一般都會輸出執行日誌。但是如果測試專案是在liunx伺服器上面執行,沒有IDE控制檯輸出log,那麼我們該如何採集日誌?元芳,你怎麼看?
日誌概述
日誌作用
不管是在專案開發還是測試過程中,專案執行一旦出現問題日誌資訊就非常重要了。日誌是定位問題的重要手段,就像偵探人員要根據現場留下的線索來推斷案情。
日誌級別
指令碼執行會有很多的情況,比如除錯資訊、報錯異常資訊等。日誌要根據這些不同的情況來繼續分級管理,不然對於排查問題的篩選會有比較大的干擾。 。日誌一般定位的級別如下:
級別 |
何時使用 |
DEBUG |
除錯資訊,也是最詳細的日誌資訊。 |
INFO |
證明事情按預期工作。 |
WARNING |
表明發生了一些意外,或者不久的將來會發生問題(如‘磁碟滿了’)。軟體還是在正常工作。 |
ERROR |
由於更嚴重的問題,軟體已不能執行一些功能了。 |
CRITICAL |
嚴重錯誤,表明軟體已不能繼續運行了。 |
首先我們日誌需要按照info、debug、error等級別來進行區分的。當然這個級別可以自己去設定。在一般的情況下我們普通的輸出我們直接用info型別,除錯的時候用debug型別,如果預計有錯誤時那麼我們就需要用error型別的日誌,一般情況去info級別最為合適。
日誌格式
日誌格式化是為了提高日誌的可閱讀性,比如:時間+模組+行數+日誌具體資訊 的內容格式。如果日誌資訊雜亂無章的全部輸出來,這樣也不利於定位問題。如下所示就是日誌格式化輸出,非常便於閱讀檢視。
2019-08-14 22:02:35,633 backup.py[line:18] INFO ============test backup================ 2019-08-14 22:02:39,253 backup.py[line:20] INFO click backup button 2019-08-14 22:02:54,025 backup.py[line:23] INFO click next button 2019-08-14 22:03:09,280 common_fun.py[line:83] INFO Start send Email.. 2019-08-14 22:03:11,840 common_fun.py[line:91] INFO Send Email finish! 2019-08-14 22:03:13,305 common_fun.py[line:168] INFO get backup screenshot 2019-08-14 23:36:00,238 backup.py[line:17] INFO ============test backup================ 2019-08-14 23:36:04,530 backup.py[line:19] INFO click backup button 2019-08-14 23:37:20,107 backup.py[line:17] INFO ============test
日誌位置
一個專案中會有很多的日誌採集點,而日誌採集點必須結合業務屬性來設定。比如在登入程式碼執行前可以插入“準備登入..”日誌資訊,如果登入完成之後,再設定登入的提示日誌就會給人造成誤解,無法判斷到底是登入之前的問題還是登入之後的問題,因此日誌採集點的位置很重要。
logging模組
簡介
Python的logging模組提供了通用的日誌系統,這個模組提供不同的日誌級別,並可以採用不同的方式記錄日誌,比如檔案,HTTP GET/POST,SMTP,Socket等,甚至可以自己實現方式記錄日誌。
#匯入logging模組
import logging
logging模組官方文件
logging構成
logging模組包括logger,Handler,Filter,Formatter四個部分。
- Logger 記錄器,用於設定日誌採集。
- Handler 處理器,將日誌記錄傳送至合適的路徑。
- Filter 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日誌記錄。
- Formatter 格式化器,指明瞭最終輸出中日誌的格式。
Logger 記錄器
Logger是一個樹形層級結構,在使用介面debug,info,warn,error,critical;使用之前必須建立Logger例項,即建立一個記錄器,如果沒有顯式的進行建立,則預設建立一個root logger,並應用預設的日誌級別(WARN),處理器Handler(StreamHandler,即將日誌資訊列印輸出在標準輸出上),和格式化器Formatter(預設的格式即為第一個簡單使用程式中輸出的格式)。
方法:
basicConfig(**kwargs) 為日誌記錄系統做基本配置。
部分引數
filename 指定日誌檔名稱
filemode 指定開啟檔案的模式,如果指定了filename(如果檔案模式未指定,則預設為'a)
Tips:檔案讀寫模式
- w 以寫方式開啟,
- W 檔案若存在,首先要清空,然後(重新)建立
- a 以追加模式開啟 (從 EOF 開始, 必要時建立新檔案)
- r+ 以讀寫模式開啟
- w+ 以讀寫模式開啟 (參見 w )
- a+ 以讀寫模式開啟 (參見 a )
format 為處理程式使用指定的格式字串。
datefmt 使用指定的日期/時間格式。樣式如果指定了格式字串,則使用它來指定 格式字串的型別.
level 將根記錄器級別設定為指定級別。
logging_test.py
# coding=utf-8 # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-8-14 @author: 北京-巨集哥 QQ交流群:707699217 Project:學習和使用appium自動化測試-程式碼和資料分離-日誌收集 ''' # 3.匯入模組 import logging # logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.INFO) logging.debug('debug info') logging.info('hello 巨集哥') logging.warning('warning info') logging.error('error info') logging.critical('critical info')
Handler 處理器
Handler 處理器,將日誌記錄傳送至合適的路徑,Handler處理器型別有很多種,比較常用的有三個:
1.StreamHandler
將日誌記錄輸出傳送到諸如sys.stdout,sys.stderr或任何類似檔案流的物件。上面例子就是輸出到控制檯
2.FileHandler
將日誌記錄輸出傳送到磁碟檔案。 它繼承了StreamHandler的輸出功能。
logging.basicConfig(filename='runlog.log',level=logging.DEBUG)
3.NullHandler
不做任何格式化或輸出。 它本質上是一個開發人員使用的“無操作”處理程式。
Filter 過濾器
Handlers和Loggers可以使用Filters來完成比級別更復雜的過濾。
Formatter
使用Formatter物件設定日誌資訊最後的規則、結構和內容,預設的時間格式為%Y-%m-%d %H:%M:%S。
格式 |
描述 |
%(levelno)s |
列印日誌級別的數值 |
%(levelname)s |
列印日誌級別名稱 |
%(pathname)s |
列印當前執行程式的路徑 |
%(filename)s |
列印當前執行程式名稱 |
%(funcName)s |
列印日誌的當前函式 |
%(lineno)d |
列印日誌的當前行號 |
%(asctime)s |
列印日誌的時間 |
%(thread)d |
列印執行緒id |
%(threadName)s |
列印執行緒名稱 |
%(process)d |
列印程序ID |
%(message)s |
列印日誌資訊 |
使用方法:
logging.basicConfig(filename='runlog.log',level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
輸出結果:
2019-08-14 14:35:19,430 logging_test.py[line:6]DEBUGdebug info 2019-08-14 14:35:19,430 logging_test.py[line:7]INFOhello hongge 2019-08-14 14:35:19,430 logging_test.py[line:8]WARNINGwarning info 2019-08-14 14:35:19,430 logging_test.py[line:9]ERRORerror info 2019-08-14 14:35:19,430 logging_test.py[line:10]CRITICALcritical info
Logging實戰操作
測試場景
將前面所學的啟動考研幫App的指令碼增加log採集功能,設定指定的日誌格式輸出,並將日誌儲存到指定檔案。
程式碼實現
kyb_logger.py
# coding=utf-8 # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-8-14 @author: 北京-巨集哥 QQ交流群:707699217 Project:學習和使用appium自動化測試-程式碼和資料分離-日誌收集 ''' # 3.匯入模組 from appium import webdriver import yaml import logging from selenium.common.exceptions import NoSuchElementException file=open('./desired_caps.yaml','r') data=yaml.load(file) logging.basicConfig(level=logging.INFO,filename='runlog.log', format='%(asctime)s %(filename)s[line:%(lineno)d]%(levelname)s%(message)s') desired_caps={} desired_caps['platformName']=data['platformName'] desired_caps['platformVersion']=data['platformVersion'] desired_caps['deviceName']=data['deviceName'] desired_caps['app']=data['app'] desired_caps['appPackage']=data['appPackage'] desired_caps['appActivity']=data['appActivity'] desired_caps['noReset']=data['noReset'] logging.info('start app...') driver=webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub',desired_caps) def check_cancelBtn(): logging.info('check cancelBtn') try: cancelBtn = driver.find_element_by_id('android:id/button2') except NoSuchElementException: logging.info('no cancelBtn') else: cancelBtn.click() def check_skipBtn(): logging.info('check skipBtn') try: skipBtn = driver.find_element_by_id('com.tal.kaoyan:id/tv_skip') except NoSuchElementException: logging.info('no skipBtn') else: skipBtn.click() check_cancelBtn() check_skipBtn()
runlog.log
2019-08-14 15:27:38,964 kyb_logger.py[line:32]INFOstart app... 2019-08-14 15:27:47,641 poolmanager.py[line:358]INFORedirecting http://127.0.0.1:4723/wd/hub/session -> http://127.0.0.1:4723/wd/hub/session/dfc8e7e7-71cc-4f0b-9aa6-5db0fdc98a84 2019-08-14 15:27:47,644 kyb_logger.py[line:36]INFOcheck cancelBtn 2019-08-14 15:27:49,442 kyb_logger.py[line:46]INFOcheck skipBtn
問題思考
前面我們已經實現了在程式碼中增添log,log也按照預期的採集到了,看似一切完美無瑕。但是該log配置的作用域也只是控制當前的指令碼 。 然而一個自動化專案中通常有很多模組指令碼,難道我們需要每一個指令碼都這樣配置嗎?元芳,你怎麼看?
解決思路
回大人,以我跟隨大人多年的斷案經驗:將這些日誌配置的引數抽離出來,各個模組需要使用則直接引用即可。
日誌格式配置
將log輸出格式,輸出路徑等引數抽離出來作為一個配置表,如下所示:
log.conf
[loggers] keys=root,infoLogger [logger_root] level=DEBUG handlers=consoleHandler,fileHandler [logger_infoLogger] handlers=consoleHandler,fileHandler qualname=infoLogger propagate=0 [handlers] keys=consoleHandler,fileHandler [handler_consoleHandler] class=StreamHandler level=INFO formatter=form02 args=(sys.stdout,) [handler_fileHandler] class=FileHandler level=INFO formatter=form01 args=('runlog.log', 'a') [formatters] keys=form01,form02 [formatter_form01] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s [formatter_form02] format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
在需要呼叫的模組增加如下程式碼:
import logging import logging.config CON_LOG='log.conf' logging.config.fileConfig(CON_LOG) logging=logging.getLogger()
方法:
fileConfig(fname, defaults=None, disable_existing_loggers=True)
該放在作用是從ConfigParser格式的檔案中讀取日誌配置,同時如果當前指令碼有配置log引數,則覆蓋當前log配置選項。
程式碼實現
kyb_logconf.py
# coding=utf-8 # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-8-14 @author: 北京-巨集哥 QQ交流群:707699217 Project:學習和使用appium自動化測試-程式碼和資料分離-日誌收集 ''' # 3.匯入模組 from appium import webdriver import yaml import logging import logging.config from selenium.common.exceptions import NoSuchElementException file=open('./desired_caps.yaml','r') data=yaml.load(file) CON_LOG='log.conf' logging.config.fileConfig(CON_LOG) logging=logging.getLogger() desired_caps={} desired_caps['platformName']=data['platformName'] desired_caps['platformVersion']=data['platformVersion'] desired_caps['deviceName']=data['deviceName'] desired_caps['app']=data['app'] desired_caps['appPackage']=data['appPackage'] desired_caps['appActivity']=data['appActivity'] desired_caps['noReset']=data['noReset'] logging.info('start app...') driver=webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub',desired_caps) def check_cancelBtn(): logging.info('check cancelBtn') try: cancelBtn = driver.find_element_by_id('android:id/button2') except NoSuchElementException: logging.info('no cancelBtn') else: cancelBtn.click() def check_skipBtn(): logging.info('check skipBtn') try: skipBtn = driver.find_element_by_id('com.tal.kaoyan:id/tv_skip') except NoSuchElementException: logging.info('no skipBtn') else: skipBtn.click() check_cancelBtn() check_skipBtn()
小結
元芳,今天分享的知識快要結束,你給總結一下,把結案文書寫一下,交給我。元芳,你怎麼看?日誌的收集。
好了好了,大人,元芳今天太累了,所以說出如此大逆不道的話,求大人原諒他,結案文書我稍後交給大人。
結案文書:
1.Logger是一個樹形層級結構
Logger可以包含一個或多個Handler和Filter,即Logger與Handler或Fitler是一對多的關係;
一個Logger例項可以新增多個Handler,一個Handler可以新增多個格式化器或多個過濾器,而且日誌級別將會繼承。
2.Logging工作流程
logging模組使用過程
- 第一次匯入logging模組或使用reload函式重新匯入logging模組,logging模組中的程式碼將被執行,這個過程中將產生logging日誌系統的預設配置。
- 自定義配置(可選)。logging標準模組支援三種配置方式: dictConfig,fileConfig,listen。其中,dictConfig是通過一個字典進行配置Logger,Handler,Filter,Formatter;fileConfig則是通過一個檔案進行配置;而listen則監聽一個網路埠,通過接收網路資料來進行配置。當然,除了以上集體化配置外,也可以直接呼叫Logger,Handler等物件中的方法在程式碼中來顯式配置。
- 使用logging模組的全域性作用域中的getLogger函式來得到一個Logger物件例項(其引數即是一個字串,表示Logger物件例項的名字,即通過該名字來得到相應的Logger物件例項)。
- 使用Logger物件中的debug,info,error,warn,critical等方法記錄日誌資訊。
您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得點波 推薦 哦!!!(點選右邊的小球即可!(^__^) 嘻嘻……)
個人公眾號 微信群 (微信群已滿100,可以加巨集哥的微信拉你進群,請備註:進群)
&n