1. 程式人生 > >Appium+python自動化(三十一)- 元芳,你怎麼看? - 日誌收集-logging(超詳解)

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模組使用過程

  1. 第一次匯入logging模組或使用reload函式重新匯入logging模組,logging模組中的程式碼將被執行,這個過程中將產生logging日誌系統的預設配置。
  2. 自定義配置(可選)。logging標準模組支援三種配置方式: dictConfig,fileConfig,listen。其中,dictConfig是通過一個字典進行配置Logger,Handler,Filter,Formatter;fileConfig則是通過一個檔案進行配置;而listen則監聽一個網路埠,通過接收網路資料來進行配置。當然,除了以上集體化配置外,也可以直接呼叫Logger,Handler等物件中的方法在程式碼中來顯式配置。
  3. 使用logging模組的全域性作用域中的getLogger函式來得到一個Logger物件例項(其引數即是一個字串,表示Logger物件例項的名字,即通過該名字來得到相應的Logger物件例項)。
  4. 使用Logger物件中的debug,info,error,warn,critical等方法記錄日誌資訊。

 

您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得點波 推薦 哦!!!(點選右邊的小球即可!(^__^) 嘻嘻……)

       個人公眾號                                                             微信群 (微信群已滿100,可以加巨集哥的微信拉你進群,請備註:進群)          

                                                                 

 

 

&n