一个自动化项目中通常有很多模块脚本,所以为了不用在每个脚本中重复配置,可以将这些日志配置的参数抽离出来各个模块需要使用则直接引用即可。
日志格式配置
将log输出格式,输出路径等参数抽离出来作为一个配置表,如下所示:
log.conf
[loggers]
#配置logger信息。必须包含一个名字叫做root的logger,当使用无参函数logging.getLogger()时,默认返回root这个logger,其他自定义logger可以通过 logging.getLogger("fileAndConsole") 方式进行调用 keys=root,infoLogger [logger_root]
# 对loggers中声明的logger进行逐个配置,且要一一对应,在所有的logger中,必须制定lebel和handlers这两个选项,对于非roothandler,还需要添加一些额外的option,其中qualname表示它在logger层级中的名字,在应用代码中通过这个名字制定所使用的handler,即 logging.getLogger("fileAndConsole"),handlers可以指定多个,中间用逗号隔开,比如handlers=fileHandler,consoleHandler,同时制定使用控制台和文件输出日志 level=DEBUG handlers=consoleHandler,fileHandler [logger_infoLogger] handlers=consoleHandler,fileHandler qualname=infoLogger propagate=0 [handlers]
#定义声明handlers信息。 keys=consoleHandler,fileHandler [handler_consoleHandler]
# 在handler中,必须指定class和args这两个option,常用的class包括 StreamHandler(仅将日志输出到控制台)、FileHandler(将日志信息输出保存到文件)、RotaRotatingFileHandler(将日志输出保存到文件中,并设置单个日志wenj文件的大小和日志文件个数),args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;里面指定输出路径,比如输出的文件名称等。level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息; 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
#调用log配置固定用法
CON_LOG='log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()
方法:
fileConfig(fname, defaults=None, disable_existing_loggers=True)
作用是从ConfigParser格式的文件中读取日志配置,同时如果当前脚本有配置log参数,则覆盖当前log配置选项。
说明,为了方便注释之间的对应,已在配置文件中添加了注释,在实际使用时,需要将里面的注释给删除,不然,可能由于中文的存在,引起异常。
在配置文件中,首先包含了三大主要模块,loggers, handlers, formatters。对于三个主要模块其包含的内容都是通过keys进行指定,然后通过logger_ke/handler_key/formatter_key对里面的key进行具体的设置。
loggers配置logger的模块,其中必须包含一个名字叫做root的logger,当在应用程序中,使用无参函数logging.getLogger()时,默认返回root这个logger,其他自定义logger可以通过 logging.getLogger("name") 方式进行调用。
handlers定义handlers的信息,通过keys进行指定。里面可以指定我们日志的输出方式、日志的级别、日志的格式等。
formatters表示设置日志的格式。
logger-XXX对loggers中声明的logger进行逐个配置,且要一一对应,在所有的logger中,必须制定lebel和handlers这两个选项,对于非roothandler,还需要添加一些额外的option,其中qualname表示它在logger层级中的名字,在应用代码中通过这个名字制定所使用的handler,即 logging.getLogger("fileAndConsole"),handlers可以指定多个,中间用逗号隔开,比如handlers=fileHandler,consoleHandler,同时制定使用控制台和文件输出日志。propagate通常设为零,这样,当我们在handlers中设置多个处理器时,不会多次打印日志信息。
handler_xxx在handler中,必须指定class和args这两个option,常用的class包括 StreamHandler(仅将日志输出到控制台)、FileHandler(将日志信息输出保存到文件)、RotaRotatingFileHandler(将日志输出保存到文件中,并设置单个日志wenj文件的大小和日志文件个数),args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;里面指定输出路径,比如输出的文件名称等。level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;
在这个配置文件中设置了三种日志的输出方式,root对应控制台输出、file对应配置文件输出、fileAndConsole对应着文件和控制台同时输出.
代码实现:
desired_caps.yaml
platformName: Android #模拟器 platformVersion: 5.1.1 deviceName: 127.0.0.1:62025 #mx4真机 #platformVersion: 5.1 #udid: 750BBKL22GDN #deviceName: MX4 appname: kaoyan3.1.0.apk noReset: False unicodeKeyboard: True resetKeyboard: True appPackage: com.tal.kaoyan appActivity: com.tal.kaoyan.ui.activity.SplashActivity ip: 127.0.0.1 port: 4723
kyb_logconf.py
#导入模块 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等方法记录日志信息。