Appium + Python 自动化测试学习之十三:Logging日志模块

您所在的位置:网站首页 日志管理可以输出哪些日志文件 Appium + Python 自动化测试学习之十三:Logging日志模块

Appium + Python 自动化测试学习之十三:Logging日志模块

2024-07-03 13:27| 来源: 网络整理| 查看: 265

日志是对软件执行时所发生事件的一种追踪方式。软件开发人员对他们的代码添加日志调用,借此来指示某事件的发生。一个事件通过一些包含变量数据的描述信息来描述(比如:每个事件发生时的数据都是不同的)。开发者还会区分事件的重要性(重要性也被称为 等级 或 严重性)。而我们在做app自动化测试时,遇到异常情况,查看日志也是必不可少的。那我们在什么情况下使用日志呢?

一.何时使用日志

对于简单的日志使用来说日志功能提供了一系列便利的函数。它们是 debug(),info(),warning(),error() 和 critical()。想要决定何时使用日志,请看下表:

你想要执行的任务此任务对应最好工具对于命令行或程序的应用,结果显示在控制台print()在对程序的普通操作发生时提交事件报告(比如:状态监控和错误调查)logging.info() 函数(当有诊断目的需要详细输出信息时使用 logging.debug() 函数)提出一个警告信息基于一个特殊的运行时事件warnings.warn() 位于代码库中,该事件是可以避免的,需要修改客户端应用以消除告警对一个特殊的运行时事件报告错误引发异常报告错误而不引发异常(如在长时间运行中的服务端进程的错误处理)logging.error(), logging.exception() 或 logging.critical() 分别适用于特定的错误及应用领域

日志功能应以所追踪事件级别或严重性而定。各级别适用性如下(以严重性递增):

级别何时使用数值DEBUG细节信息,仅当诊断问题时适用。10INFO确认程序按预期运行20WARNING表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行30ERROR由于严重的问题,程序的某些功能已经不能正常执行40CRITICAL严重的错误,表明程序已不能继续执行50

默认的级别是WARNING,意味着只会追踪该级别及以上的事件,除非更改日志级别配置。 例子:

import logging logging.warning('This is waring!') # 会在控制台输出This is waring! logging.info('This is info') # 不会输出任何内容

那么如果想把日志文件记录到指定的文件件呢,该如何配置?

# -*- coding:utf-8 -*- import logging # 通过下面的方式进行简单配置输出方式与日志级别 logging.basicConfig(filename='logs.log', level=logging.DEBUG,) logging.debug('debug message') logging.info('info message') logging.warning('warn message') logging.error('error message') logging.critical('critical message')

控制台未显示任何信息,而在当前工作目录下生成了logs.log,内容如下:

DEBUG:root:debug message INFO:root:info message WARNING:root:warn message ERROR:root:error message CRITICAL:root:critical message 二.重要概念 Logger 记录器:暴露了应用程序代码能直接使用的接口。Handler 处理器:将(记录器产生的)日志记录发送至合适的目的地。Filter 过滤器:提供了更好的粒度控制,它可以决定输出哪些日志记录。Formatter 格式化器:指明了最终输出中日志记录的布局。 2.1 Logger处理器

Logger类永远不会直接实例化,而是始终通过模块级函数实例化 logging.getLogger(name),getLogger()具有相同名称的多次调用将始终返回对同一Logger对象的引用。 如果没有显式创建,默认创建一个名称为root的logger,并应用默认的日志级别(WARN),默认处理器Handler(StreamHandler,即屏幕打印),和默认格式化器Formatter(日志级别:logger名称:消息)。 创建方式:

#实例化一个logger对象 logger = logging.getLogger(name)

创建Logger实例后,可以使用以下方法进行日志级别设置,增加处理器Handler。

logger.setLevel(logging.ERROR) :设置日志级别为ERROR,即只有日志级别大于等于ERROR的日志才会输出logger.addHandler(handler_name) :为Logger实例增加一个处理器logger.removeHandler(handler_name) : 为Logger实例删除一个处理器 主要函数用法setLevel(level)设置记录器级别,高于level的日志才会被记录。如果Handler设置了更高的日志级别,则以Handler日志级别为准。getChild(suffix)返回后代记录器,logging.getLogger(‘abc’).getChild(‘def.ghi’)与logging.getLogger(‘abc.def.ghi’)一致。addFilter(filter)添加过滤器removeFilter(filter)删除过滤器addHandler(handler)增加处理器removeHandler(handler)删除处理器 2.2 Handler 处理器

Handler处理器类型有很多种,比较常用的有四个:StreamHanlder, FileHandler,RotatingFileHandler,TimedRotatingFileHandler。

Handler类型主要用途StreamHandler输出日志到标准输出设备(如屏幕)FileHandler输出日志到文件RotatingFileHandler输出到文件,支持文件达到一定大小后创建一个新的日志文件TimedRotatingFileHandler输出到文件,支持经过一段时间后创建一个新的日志文件

创建StreamHandler之后,可以通过使用以下方法设置日志级别,设置格式化器Formatter,增加或删除过滤器Filter。

sh.setLevel(logging.WARN) : 指定日志级别,低于WARN级别的日志将被忽略sh.setFormatter(formatter_name) :设置一个格式化器formattersh.addFilter(filter_name) : 增加一个过滤器,可以增加多个sh.removeFilter(filter_name) : 删除一个过滤器

StreamHandler——流处理器

创建方式:

#实例化控制台输出对象 sh = logging.StreamHandler()

FileHandler——文件处理器

创建方式:

#实例化文件输出对象 fh = logging.FileHandler(filename, mode='a', encoding='UTF-8', delay=False)

RotatingFileHandler——循环文件处理器 长期运行的程序(如web服务),日志文件会越来越大,大到可能难以读写——循环处理器就是解决这一问题——自动切分日志。 创建方式:

#maxBytes是当前日志最大字节数,backupCount是老日志保留个数 rh = logging.RotatingFileHandler(filename,mode=‘a’,maxBytes=0, backupCount=0, encoding=None, delay=False) 如果maxBytes或者backupCount任意一个等于0,则永不会触发循环,日志无限增长。如果当前日志达到设定的非零maxBytes,则新建一个日志文件,老的日志文件被重命名为后缀‘.1’,’.2’…等,如果新的日志又达到设定大小,则再新建一个日志,当前所有backup日志的后缀+1,最多保持backupCount个后缀,注意当前日志是没有后缀的。

TimeRotatingFileHandler——时间循环文件处理器

创建方式:

#when和interval共同决定循环时间间隔,when是单位,interval是数量。循环后老日志会自动加上一个后缀,当前日志是没有后缀的。 rh = logging.TimedRotatingFileHandler(filename, when=‘h’, interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None) 当when设置为W0到W6时,interval失效。backupCount=0则永远不会删除老日志。系统将老日志自动保存,添加一个以’%Y-%m-%d_%H-%M-%S’格式的拓展名。如果when设置成了1分钟,但一分钟内程序没有打印日志,则也不会自动循环(这个好理解,不然会造成很多空日志文件)。 2.3 Formatter格式化器

使用Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S。

创建方法:

#fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用'%(message)s'。如果不指明datefmt,将使用ISO8601日期格式。 formatter = logging.Formatter(fmt=None, datefmt=None)

其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用’%(message)s’。如果不指明datefmt,将使用ISO8601日期格式。

日志格式含义%(levelno)s打印日志级别的数值%(levelname)s打印日志级别名称%(pathname)s打印当前执行程序的路径%(filename)s打印当前执行程序名称%(funcName)s打印日志的当前函数%(lineno)d打印日志的当前行号%(asctime)s打印日志的时间%(thread)d打印线程id%(threadName)s打印线程名称%(process)d打印进程ID%(message)s打印日志信息%(module)s当前模块名%(name)s打印这条消息的Logger名 2.4 Filter 过滤器

Handlers和Loggers可以使用Filters来完成比级别更复杂的过滤。Filter基类只允许特定Logger层次以下的事件。例如用‘A.B’初始化的Filter允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。

创建方式:

#包含了name的记录将会被过滤掉,不管是什么日志级别;如name=’’,则不过滤。 filter= logging.Filter(name=’’)

它们之间的关系:Logger可以包含一个或多个Handler和Filter,一个Handler可以新增多个Formatter和Filter,而且日志级别将会继承。

三. Logging 封装

实例:

import logging import os PATA=lambda p:os.path.abspath(os.path.join(os.path.dirname(__file__),p)) class Log: def __init__(self,name=None): self.logger=logging.getLogger(name)#实例化一个logger对象 self.logformat=logging.Formatter('%(asctime)s - %(filename)s- %(name)s - %(levelname)s - %(message)s') self.logger.setLevel(logging.DEBUG) def __console(self,level,message): if not self.logger.handlers: fh=logging.FileHandler(PATA('../logfile/mylog.txt'),'a',encoding='utf-8')#实例化文件输出对象 fh.setFormatter(self.logformat)#增加输出格式 fh.setLevel(logging.DEBUG)#设置日志级别 self.logger.addHandler(fh) # 增加文件输出对象 if level.lower()=='info': self.logger.info(message) elif level.lower()=='debug': self.logger.debug(message) elif level.lower()=='warning': self.logger.warning(message) elif level.lower()=='error': self.logger.error(message) else: self.logger.exception(message) self.logger.removeHandler(fh)#避免日志输出重复 fh.close()#关闭打开的日志文件 def info(self,message): self.__console('info',message) def debug(self,message): self.__console('debug',message) def warning(self,message): self.__console('warning',message) def error(self,message): self.__console('error',message) def exception(self,message): self.__console('exception',message) if __name__ == '__main__': log=Log('test') log.info('info测试') log.debug('debug') log.warning('warning') log.error('error') 四.多模块下使用

多次调用 logging.getLogger(‘someLogger’) 时会返回对同一个 logger 对象的引用。 这不仅是在同一个模块中,在其他模块调用也是如此,只要是在同一个 Python 解释器进程中。 是应该引用同一个对象,此外,应用程序也可以在一个模块中定义和配置父 logger,而在单独的模块中创建(但不配置)子 logger,对子 logger 的所有调用都将传给父 logger。 这里是主模块:

import logging import auxiliary_module import os PATA=lambda p:os.path.abspath(os.path.join(os.path.dirname(__file__),p)) #实例化名为“auto_test”的logger对象 logger = logging.getLogger('auto_test') logger.setLevel(logging.DEBUG) # 实例化文件输出对象 fh = logging.FileHandler('spam.log') fh.setLevel(logging.DEBUG) # 实例化控制台输出对象 ch = logging.StreamHandler() ch.setLevel(logging.ERROR) # 设置输出格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') #设置文件和控制台输出格式 fh.setFormatter(formatter) ch.setFormatter(formatter) # 增加文件和控制台输出对象 logger.addHandler(fh) logger.addHandler(ch) logger.info('creating an instance of auxiliary_module.Auxiliary') a = auxiliary_module.Auxiliary() logger.info('created an instance of auxiliary_module.Auxiliary') logger.info('calling auxiliary_module.Auxiliary.do_something') a.do_something() logger.info('finished auxiliary_module.Auxiliary.do_something') logger.info('calling auxiliary_module.some_function()') auxiliary_module.some_function() logger.info('done with auxiliary_module.some_function()')

这里是辅助模块:

import logging # create logger module_logger = logging.getLogger('spam_application.auxiliary') class Auxiliary: def __init__(self): self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary') self.logger.info('creating an instance of Auxiliary') def do_something(self): self.logger.info('doing something') a = 1 + 1 self.logger.info('done doing something') def some_function(): module_logger.info('received a call to "some_function"') 五.配置文件配置日志

这种配置方式可以将日志配置和代码分离,方便代码的维护和日志管理。

实现logger对应的配置信息必须是 logger_name: name为loggers中key的值level : 日志级别,级别有 DEBUG,INFO,WARNING,ERROR,CRITICALhandlers : 日志处理器,可以有多个 以逗号隔开qualname : logger的名称,通过logging.getLogger(name)获取,这里的name便是qualname,如果获取的logger 名称不存在,则调用默认(root)loggerpropagate 是否继承父类的配置信息,0:否 1:是

新建配置文件logging.conf,用于存放logging配置文件的信息:

# 配置logger信息。必须包含一个名字叫做root的logger,当使用无参函数logging.getLogger()时, #默认返回root这个logger,其他自定义logger可以通过 logging.getLogger("fileAndConsole") 方式进行调用 [loggers] keys=root,file,fileAndConsole # 定义声明handlers信息。 [handlers] keys=fileHandler,consoleHandler # 设置日志格式 [formatters] keys=simpleFormatter # 对loggers中声明的logger进行逐个配置,且要一一对应,在所有的logger中,必须制定level和handlers这两个选项,对于非roothandler, #还需要添加一些额外的option,其中qualname表示它在logger层级中的名字,在应用代码中通过这个名字制定所使用的handler, #即 logging.getLogger("fileAndConsole"),handlers可以指定多个,中间用逗号隔开,比如handlers=fileHandler,consoleHandler, #同时制定使用控制台和文件输出日志 [logger_root] level=DEBUG handlers=consoleHandler [logger_file] level=DEBUG handlers=fileHandler qualname=file propagate=1 [logger_fileAndConsole] level=DEBUG handlers=fileHandler,consoleHandler qualname=fileAndConsole propagate=0 # 在handler中,必须指定class和args这两个option,常用的class包括 StreamHandler(仅将日志输出到控制台)、FileHandler(将日志信息输出保存到文件)、 #RotaRotatingFileHandler(将日志输出保存到文件中,并设置单个日志wenj文件的大小和日志文件个数), #args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式; #里面指定输出路径,比如输出的文件名称等。level与logger中的level一样,而formatter指定的是该处理器所使用的格式器, #这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义; #如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息; [handler_consoleHandler] class=StreamHandler args=(sys.stdout,) level=DEBUG formatter=simpleFormatter [handler_fileHandler] class=FileHandler args=('loggers.log', 'a') level=DEBUG formatter=simpleFormatter [formatter_simpleFormatter] format=%(asctime)s - %(module)s - %(thread)d - %(levelname)s : %(message)s datefmt=%Y-%m-%d %H:%M:%S

logging日志文件的使用:

import logging import logging.config logging.config.fileConfig('logger.conf') logger=logging.getLogger() logger.debug('debug message') logger.info('info message')


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3