簡介
生活中的日志是記錄你生活的點點滴滴,讓它把你內(nèi)心的世界表露出來,更好的詮釋自己的內(nèi)心世界,而電腦里的日志是有價值的信息寶庫。
日志文件是專門用于記錄系統(tǒng)操作事件的記錄文件或文件集合,操作系統(tǒng)有操作系統(tǒng)日志文件,數(shù)據(jù)庫系統(tǒng)有數(shù)據(jù)庫系統(tǒng)日志文件,等等。
系統(tǒng)日志文件是包含關(guān)于系統(tǒng)消息的文件,包括內(nèi)核、服務(wù)、在系統(tǒng)上運行的應(yīng)用程序等。不同的日志文件記載不同的信息。例如,有的是默認(rèn)的系統(tǒng)日志文件,有的記載特定任務(wù)。
在數(shù)據(jù)庫中用事務(wù)日志文件記錄數(shù)據(jù)的修改操作,其中的每條日志記錄或者記錄所執(zhí)行的邏輯操作,或者記錄已修改數(shù)據(jù)的前像和后像。前像是操作執(zhí)行前的數(shù)據(jù)復(fù)本;后像是操作執(zhí)行后的數(shù)據(jù)復(fù)本。
問題思考
在自動化腳本運行過程中,IDE控制臺一般都會輸出運行日志。但是如果測試項目是在liunx服務(wù)器上面運行,沒有IDE控制臺輸出log,那么我們該如何采集日志?
元芳,你怎么看?
? ? ???
日志概述
日志作用
不管是在項目開發(fā)還是測試過程中,項目運行一旦出現(xiàn)問題日志信息就非常重要了。日志是定位問題的重要手段,就像偵探人員要根據(jù)現(xiàn)場留下的線索來推斷案情。
日志級別
腳本運行會有很多的情況,比如調(diào)試信息、報錯異常信息等。日志要根據(jù)這些不同的情況來繼續(xù)分級管理,不然對于排查問題的篩選會有比較大的干擾。
。日志一般定位的級別如下:
?
級別
何時使用
DEBUG
調(diào)試信息,也是最詳細(xì)的日志信息。
INFO
證明事情按預(yù)期工作。
WARNING
表明發(fā)生了一些意外,或者不久的將來會發(fā)生問題(如‘磁盤滿了’)。軟件還是在正常工作。
ERROR
由于更嚴(yán)重的問題,軟件已不能執(zhí)行一些功能了。
CRITICAL
嚴(yán)重錯誤,表明軟件已不能繼續(xù)運行了。
首先我們?nèi)罩拘枰凑?br>info、debug、error等級別來進(jìn)行區(qū)分的。當(dāng)然這個級別可以自己去設(shè)置。在一般的情況下我們普通的輸出我們直接用info類型,調(diào)試的時候用debug類型,如果預(yù)計有錯誤時那么我們就需要用error類型的日志,一般情況去info級別最為合適。
日志格式
日志格式化是為了提高日志的可閱讀性,比如:時間+模塊+行數(shù)+日志具體信息
?的內(nèi)容格式。如果日志信息雜亂無章的全部輸出來,這樣也不利于定位問題。如下所示就是日志格式化輸出,非常便于閱讀查看。
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 button2019-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
日志位置
一個項目中會有很多的日志采集點,而日志采集點必須結(jié)合業(yè)務(wù)屬性來設(shè)置。比如在登錄代碼執(zhí)行前可以插入
“準(zhǔn)備登錄..”日志信息,如果登錄完成之后,再設(shè)置登錄的提示日志就會給人造成誤解,無法判斷到底是登錄之前的問題還是登錄之后的問題,因此日志采集點的位置很重要。
logging模塊
簡介
Python的logging模塊提供了通用的日志系統(tǒng),這個模塊提供不同的日志級別,并可以采用不同的方式記錄日志,比如文件,HTTP
GET/POST,SMTP,Socket等,甚至可以自己實現(xiàn)方式記錄日志。
#導(dǎo)入logging模塊
import?logging
logging模塊官方文檔 <https://docs.python.org/3.5/library/logging.html?>
logging構(gòu)成
logging模塊包括logger,Handler,F(xiàn)ilter,F(xiàn)ormatter四個部分。
* Logger 記錄器,用于設(shè)置日志采集。
* Handler 處理器,將日志記錄發(fā)送至合適的路徑。
* Filter 過濾器,提供了更好的粒度控制,它可以決定輸出哪些日志記錄。
* Formatter 格式化器,指明了最終輸出中日志的格式。
Logger 記錄器
Logger是一個樹形層級結(jié)構(gòu),在使用接口debug,info,warn,error,critical;使用之前必須創(chuàng)建Logger實例,即創(chuàng)建一個記錄器,如果沒有顯式的進(jìn)行創(chuàng)建,則默認(rèn)創(chuàng)建一個root
logger,并應(yīng)用默認(rèn)的日志級別(WARN),處理器Handler(StreamHandler,即將日志信息打印輸出在標(biāo)準(zhǔn)輸出上),和格式化器Formatter(默認(rèn)的格式即為第一個簡單使用程序中輸出的格式)。
方法:
basicConfig(**kwargs) 為日志記錄系統(tǒng)做基本配置。
部分參數(shù)
filename?指定日志文件名稱
filemode?指定打開文件的模式,如果指定了filename(如果文件模式未指定,則默認(rèn)為'a)
Tips:文件讀寫模式
* w 以寫方式打開,
* W 文件若存在,首先要清空,然后(重新)創(chuàng)建
* a 以追加模式打開 (從 EOF 開始, 必要時創(chuàng)建新文件)
* r+ 以讀寫模式打開
* w+ 以讀寫模式打開 (參見 w )
* a+ 以讀寫模式打開 (參見 a )
format?為處理程序使用指定的格式字符串。
datefmt?使用指定的日期/時間格式。樣式如果指定了格式字符串,則使用它來指定 格式字符串的類型.
level?將根記錄器級別設(shè)置為指定級別。
logging_test.py
# coding=utf-8 # 1.先設(shè)置編碼,utf-8可支持中英文,如上,一般放在第一行 # 2.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。 '''
Created on2019-8-14 @author: 北京-宏哥 QQ交流群:707699217 Project:學(xué)習(xí)和使用appium自動化測試
-代碼和數(shù)據(jù)分離-日志收集 ''' # 3.導(dǎo)入模塊 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 處理器,將日志記錄發(fā)送至合適的路徑,Handler處理器類型有很多種,比較常用的有三個:
1.StreamHandler
將日志記錄輸出發(fā)送到諸如sys.stdout,sys.stderr或任何類似文件流的對象。上面例子就是輸出到控制臺
2.FileHandler
將日志記錄輸出發(fā)送到磁盤文件。 它繼承了StreamHandler的輸出功能。
logging.basicConfig(filename='runlog.log',level=logging.DEBUG)
3.NullHandler
不做任何格式化或輸出。 它本質(zhì)上是一個開發(fā)人員使用的“無操作”處理程序。
Filter 過濾器
Handlers和Loggers可以使用Filters來完成比級別更復(fù)雜的過濾。
Formatter
使用Formatter對象設(shè)置日志信息最后的規(guī)則、結(jié)構(gòu)和內(nèi)容,默認(rèn)的時間格式為%Y-%m-%d %H:%M:%S。
格式
描述
%(levelno)s
打印日志級別的數(shù)值
%(levelname)s
打印日志級別名稱
%(pathname)s
打印當(dāng)前執(zhí)行程序的路徑
%(filename)s
打印當(dāng)前執(zhí)行程序名稱
%(funcName)s
打印日志的當(dāng)前函數(shù)
%(lineno)d
打印日志的當(dāng)前行號
%(asctime)s
打印日志的時間
%(thread)d
打印線程id
%(threadName)s
打印線程名稱
%(process)d
打印進(jìn)程ID
%(message)s
打印日志信息
使用方法:
logging.basicConfig(filename='runlog.log',level=logging.DEBUG, format='
%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')? ? ? ? ?
? ? ?
輸出結(jié)果:
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實戰(zhàn)操作
測試場景
將前面所學(xué)的啟動考研幫App的腳本增加log采集功能,設(shè)置指定的日志格式輸出,并將日志保存到指定文件。
代碼實現(xiàn)
kyb_logger.py
# coding=utf-8 # 1.先設(shè)置編碼,utf-8可支持中英文,如上,一般放在第一行 # 2.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。 '''
Created on2019-8-14 @author: 北京-宏哥 QQ交流群:707699217 Project:學(xué)習(xí)和使用appium自動化測試
-代碼和數(shù)據(jù)分離-日志收集 ''' # 3.導(dǎo)入模塊 from appium import webdriver import yaml import
loggingfrom 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
問題思考
前面我們已經(jīng)實現(xiàn)了在代碼中增添log,log也按照預(yù)期的采集到了,看似一切完美無瑕。但是該log配置的作用域也只是控制當(dāng)前的腳本 。
然而一個自動化項目中通常有很多模塊腳本,難道我們需要每一個腳本都這樣配置嗎?元芳,你怎么看?
?
解決思路
回大人,以我跟隨大人多年的斷案經(jīng)驗:將這些日志配置的參數(shù)抽離出來,各個模塊需要使用則直接引用即可。
日志格式配置
將log輸出格式,輸出路徑等參數(shù)抽離出來作為一個配置表,如下所示:
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
在需要調(diào)用的模塊增加如下代碼:
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格式的文件中讀取日志配置,同時如果當(dāng)前腳本有配置log參數(shù),則覆蓋當(dāng)前l(fā)og配置選項。
代碼實現(xiàn)
kyb_logconf.py
# coding=utf-8 # 1.先設(shè)置編碼,utf-8可支持中英文,如上,一般放在第一行 # 2.注釋:包括記錄創(chuàng)建時間,創(chuàng)建人,項目名稱。 '''
Created on2019-8-14 @author: 北京-宏哥 QQ交流群:707699217 Project:學(xué)習(xí)和使用appium自動化測試
-代碼和數(shù)據(jù)分離-日志收集 ''' # 3.導(dǎo)入模塊 from appium import webdriver import yaml import
logging import logging.configfrom 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()
?小結(jié)
元芳,今天分享的知識快要結(jié)束,你給總結(jié)一下,把結(jié)案文書寫一下,交給我。元芳,你怎么看?日志的收集。
?好了好了,大人,元芳今天太累了,所以說出如此大逆不道的話,求大人原諒他,結(jié)案文書我稍后交給大人。
結(jié)案文書:
1.Logger是一個樹形層級結(jié)構(gòu)
Logger可以包含一個或多個Handler和Filter,即Logger與Handler或Fitler是一對多的關(guān)系;
一個Logger實例可以新增多個Handler,一個Handler可以新增多個格式化器或多個過濾器,而且日志級別將會繼承。
?
?
2.Logging工作流程
logging模塊使用過程
*
第一次導(dǎo)入logging模塊或使用reload函數(shù)重新導(dǎo)入logging模塊,logging模塊中的代碼將被執(zhí)行,這個過程中將產(chǎn)生logging日志系統(tǒng)的默認(rèn)配置。
* 自定義配置(可選)。logging標(biāo)準(zhǔn)模塊支持三種配置方式:
dictConfig,fileConfig,listen。其中,dictConfig是通過一個字典進(jìn)行配置Logger,Handler,F(xiàn)ilter,F(xiàn)ormatter;fileConfig則是通過一個文件進(jìn)行配置;而listen則監(jiān)聽一個網(wǎng)絡(luò)端口,通過接收網(wǎng)絡(luò)數(shù)據(jù)來進(jìn)行配置。當(dāng)然,除了以上集體化配置外,也可以直接調(diào)用Logger,Handler等對象中的方法在代碼中來顯式配置。
*
使用logging模塊的全局作用域中的getLogger函數(shù)來得到一個Logger對象實例(其參數(shù)即是一個字符串,表示Logger對象實例的名字,即通過該名字來得到相應(yīng)的Logger對象實例)。
* 使用Logger對象中的debug,info,error,warn,critical等方法記錄日志信息。
?
您的肯定就是我進(jìn)步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得點波?推薦?哦?。。。c擊右邊的小球即可!(^__^) 嘻嘻……)
? ? ? ?個人公眾號? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?微信群
?(微信群已滿100,可以加宏哥的微信拉你進(jìn)群,請備注:進(jìn)群)? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?
?
?
熱門工具 換一換