我们在做测试的时候,如果哪里出现了bug,都会简单的看下日志,日志的好看和详细与否对我们这些发现问题的和查看问题的都非常重要,那么在自动化中如果把自己的日志完善起来呢?我们可以通过logging模块来帮助我们完善自动化测试过程的中日志。
logging模块
logging模块中包含了多种打印日志的方法,可以通过不同的错误,打印不同的日志
logging大概分为4个组件
组件名称 | 对应类名 | 功能描述 |
---|---|---|
日志器 | Logger | 提供了应用程序可一直使用的接口 |
处理器 | Handler | 将logger创建的日志记录发送到合适的目的输出 |
过滤器 | Filter | 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录 |
格式器 | Formatter | 决定日志记录的最终输出格式 |
日志的分级
日志级别分为NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
CRITICAL = 50 FATAL = CRITICAL ERROR = 40 WARNING = 30 WARN = WARNING INFO = 20 DEBUG = 10 NOTSET = 0
简单的日志输出
# coding:utf-8 import logging logging.debug('调试模式') # 调试模式 logging.info('基础信息') # 基础信息 logging.warning('警告信息') # 警告 logging.error('错误信息') # 错误 logging.critical('严重错误信息') # 严重错误
WARNING:root:警告信息
ERROR:root:错误信息
CRITICAL:root:严重错误信息
通过执行发现调试模式和基础模式的信息内容没有打印出来,这是为什么呢?logging模块中默认打印警告以上的日志信息,如果想要打印调试的和基础信息,我们可以设置打印信息级别 logging.basicConfig(level=logging.DEBUG)
# coding:utf-8 import logging # 设置打印日志级别 logging.basicConfig(level=logging.DEBUG) logging.debug('调试模式') # 调试模式 logging.info('基础信息') # 基础信息 logging.warning('警告信息') # 警告 logging.error('错误信息') # 错误 logging.critical('严重错误信息') # 严重错误 ---------结果------ DEBUG:root:调试模式 INFO:root:基础信息 WARNING:root:警告信息 ERROR:root:错误信息 CRITICAL:root:严重错误信息
输出格式
想要日志完全,光有详细的日志也不行,也要有好看的格式,我们可以通过logging下的format来设置打印的输出格式
# coding:utf-8 import logging # 设置打印日志级别 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',) logging.debug('调试模式') # 调试模式 logging.info('基础信息') # 基础信息 logging.warning('警告信息') # 警告 logging.error('错误信息') # 错误 logging.critical('严重错误信息') # 严重错误 ------------------结果-------------------- 2020-03-24 18:20:06,818 A3.py[line:6] DEBUG 调试模式 2020-03-24 18:20:06,818 A3.py[line:7] INFO 基础信息 2020-03-24 18:20:06,818 A3.py[line:8] WARNING 警告信息 2020-03-24 18:20:06,819 A3.py[line:9] ERROR 错误信息 2020-03-24 18:20:06,819 A3.py[line:10] CRITICAL 严重错误信息
如果信息不够的话,我们也可以添加其他内容
字段/属性名称 | 使用格式 | 描述 |
---|---|---|
asctime | %(asctime)s | 日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896 |
created | %(created)f | 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值 |
relativeCreated | %(relativeCreated)d | 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(目前还不知道干嘛用的) |
msecs | %(msecs)d | 日志事件发生事件的毫秒部分 |
levelname | %(levelname)s | 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') |
levelno | %(levelno)s | 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50) |
name | %(name)s | 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger |
message | %(message)s | 日志记录的文本内容,通过 msg % args 计算得到的 |
pathname | %(pathname)s | 调用日志记录函数的源码文件的全路径 |
filename | %(filename)s | pathname的文件名部分,包含文件后缀 |
module | %(module)s | filename的名称部分,不包含后缀 |
lineno | %(lineno)d | 调用日志记录函数的源代码所在的行号 |
funcName | %(funcName)s | 调用日志记录函数的函数名 |
process | %(process)d | 进程ID |
processName | %(processName)s | 进程名称,Python 3.1新增 |
thread | %(thread)d | 线程ID |
threadName | %(thread)s | 线程名称 |
保存在文件
日志输入的详细也有了,完整的格式也有了,那么如果我们想要保存在本地文件中呢?只需要加入filename参数就行了,但是这种无法在输出台上打印日志
# coding:utf-8 import logging # 设置打印日志级别 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', filename='test.log') logging.debug('debug') # 调试模式 logging.info('info') # 基础信息 logging.warning('warning') # 警告 logging.error('error') # 错误 logging.critical('FATAL') # 严重错误
handlers
handlers可以将log合理的打印到指定的位置,其中我们handlers用到最多的地方就是StreamHandler和FileHandler
handlers其他用法
Handler | 描述 |
---|---|
logging.StreamHandler | 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。 |
logging.FileHandler | 将日志消息发送到磁盘文件,默认情况下文件大小会无限增长 |
logging.handlers.RotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按大小切割 |
logging.hanlders.TimedRotatingFileHandler | 将日志消息发送到磁盘文件,并支持日志文件按时间切割 |
logging.handlers.HTTPHandler | 将日志消息以GET或POST的方式发送给一个HTTP服务器 |
logging.handlers.SMTPHandler | 将日志消息发送给一个指定的email地址 |
logging.NullHandler | 该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。 |
了解了Handre之后,我们可以通过handre来写一个既可以打印到输出台,也可以打印到日志文件中
import logging # 创建一个logger对象 logger = logging.getLogger() # 创建一个文件管理操作符 fh = logging.FileHandler('logger.log',encoding='utf-8') # 创建一个输出台操作符 sh = logging.StreamHandler() # 创建一个日志输出的格式 format1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 文件管理操作符 绑定一个 格式 fh.setFormatter(format1) # 输出台操作符 绑定一个 格式 sh.setFormatter(format1) logger.setLevel(logging.DEBUG) # logger对象 绑定 文件管理操作符 logger.addHandler(fh) # logger对象 绑定 输出台操作符 logger.addHandler(sh) logger.debug('debug') # 调试模式 logger.info('info信息') # 基础信息 logger.warning('warning') # 警告 logger.error('error') # 错误 logger.critical('critical')# 严重错误
这样的话就可以分别在输出台上和文件中同时显示了
封装Log文件
我们可以封装一个简单的log文件,然后我们写自动化测试的时候,可以直接进行调用
# coding:utf-8 import logging,time import os # log_path是存放日志的路径 cur_path = os.path.dirname(os.path.realpath(__file__)) log_path = os.path.join(os.path.dirname(cur_path), 'logs') # 如果不存在这个logs文件夹,就自动创建一个 if not os.path.exists(log_path): os.mkdir(log_path) class Logger(object): def __init__(self,name): # 文件的命名 self.logname = os.path.join(log_path, '%s.log'%time.strftime('%Y_%m_%d')) self.logger = logging.getLogger(name) self.logger.setLevel(logging.DEBUG) # 日志输出格式 self.formatter = logging.Formatter('[%(asctime)s]-%(name)s-%(levelname)s-%(message)s') # 创建一个FileHandler,存储日志文件 fh = logging.FileHandler(self.logname, encoding='utf-8') fh.setLevel(logging.DEBUG) fh.setFormatter(self.formatter) self.logger.addHandler(fh) # 创建一个StreamHandler,用于输出到控制台 sh = logging.StreamHandler() sh.setLevel(logging.DEBUG) sh.setFormatter(self.formatter) self.logger.addHandler(sh) def info(self,message): self.logger.info(message) def debug(self,message): self.logger.debug(message) def warning(self,message): self.logger.warning(message) def error(self,message): self.logger.error(message) def Fatal(self,message): self.logger.critical(message) if __name__ == '__main__': log = Logger('Anjing') log.info('基础信息') log.debug('调试信息') log.warning('警告信息') log.error('错误信息')
封装日志的方法很多,怎么方面怎么进行封装。