上期介绍了Python的一些基本配置,本期将介绍一下本人认为在编码和测试中很重要的一个点,那就是log。log可以记录我们的所有痕迹,所谓“燕过留痕,风过留声”,log不仅可以记录bug信息,通过log分析还可以知道我们设计的逻辑是否正确。有时候经常会出现一种情况,开发的自己明明是这么想的,执行的时候输出的结果却和我们想象中的不一样,这个时候我们就可以通过log去一步步debug,看看代码是怎么运行的,结果是如何一步步呈现给我们的,从中我们可以找到出现错误的根源,因为有时候并不一定是代码出了问题,可能是我们设计的逻辑出了问题,这时候用IDE的debug方式并不好用。
Python标准库里的logging库是Python 的日志记录工具,这里推荐使用logging模块的主要好处是所有的Python模块都可以参与日志记录,并且有很多功能比较灵活。官方文档参考地址:logging --- Python 的日志记录工具 — Python 3.10.12 文档。可以选择不同版本去查阅。先举个简单例子:
>>>importlogging>>>logging.warning('Watch out!') WARNING:root:Watchout!
上面例子里就是logging的基础用法,先引入模块,然后根据不同的级别输出log信息。logging记录日志分级别,分别是critical > error > warning > info > debug,一共五个等级,级别越高打印的日志越少,反之亦然,可以参考如下详细说明:
- debug : 打印全部的日志
- info : 打印 info, warning, error, critical 级别的日志
- warning : 打印 warning, error, critical 级别的日志
- error : 打印 error, critical 级别的日志
- critical : 打印 critical 级别
比如上面例子里的level是warning级别的日志,当日志等级设置在warning之前时,日志就会打印,默认不设置级别的情况下只打印大于等于warning级别的日志。
logging模块提供两种记录日志的方式,第一种是使用logging提供的模块级别的函数,第二种是使用logging的四大组件。
先看Logging 定义的模块级别函数,通过logging.basicConfig()函数设置日志级别和输出格式,并且需要在开头就设置,在中间设置不起作用。
logging.basicConfig(filename='./log.log',level=logging.DEBUG)
在上面例子中,除了设置level外,还定义了将日志记录到文件,在根目录下会生成一个log.log的文件,里面会记录日志信息。
如果有多个模块,可以在一个模块中import另一个模块用日志来显示另一个模块的信息。简单举例:
a.py模块
importloggingimportblogging.basicConfig(filename='a.log',level=logging.DEBUG) logging.info('Welcome') b.run() logging.info('Done')
b.py模块
importlogginglogging.info('Go')
执行a.py模块会打印相应的日志,在日志文件a.log中会显示信息如下:
INFO:root:WelcomeINFO:root:GoINFO:root:Done
另外可以根据实际需求修改日志显示的格式,比如加入日期时间信息。
importlogginglogging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S') logging.warning('Warning')
执行结果:
07/20/202305:51:27Warning
再来说说logging模块的四大组件,日志器Logger,处理器Handler,过滤器Filter,格式器Formatter。
Logger是通过模块级函数logging.getLogger(name)来实例化,创建方法:
logger=logging.getLogger(logger_name) logger.setLevel=(logging.DEBUG)
推荐加上日志记录的模块名,多次使用相同name调用getLogger方法会返回同一个logger对象,即相同的log会记录多次。
Handler处理器类型有很多种,常用的有StreamHandler,FileHandler。可以用StreamHandler输出日志到控制台,用FileHandler写入日志文件,用setFormatter分别设置合适的格式,并将两个处理器加入logger实例中。
file_handler=logging.FileHandler('./log.log, mode='w')console_handler=logging.StreamHandler() file_handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')) console_handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')) logger.addHandler(file_handler) logger.addHandler(console_handler)
Filter比较少用,引用一下官方解释,感兴趣可以去阅读文档。
Filters 可被 Handlers 和 Loggers 用来实现比按层级提供更复杂的过滤操作。 基本过滤器类只允许低于日志记录器层级结构中低于特定层级的事件。 例如,一个用 'A.B' 初始化的过滤器将允许 'A.B', 'A.B.C', 'A.B.C.D', 'A.B.D' 等日志记录器所记录的事件。 但 'A.BB', 'B.A.B' 等则不允许。 如果用空字符串初始化,则所有事件都会通过。
https://docs.python.org/zh-cn/3.10/library/logging.html#filter-objects
Formatter可以让我们自己定义日志信息的输出规则,结构和内容,可以结合实际情况来设置,一般日期,模块名之类的都推荐加进来,这样便于查看日志时了解事件发生的时间和地点。
四个组件之间都是相互关联的,可以简单的理解一下,logger是建立一个接口,handler用来干实事儿,filter和formatter来制定一些规则。从上面的例子还可以看出,logger可以包含多个handler,日志级别可以分开设置并继承,比如对于记录到log文件的日志,我们级别可以设置为debug,把全部日志都记录下来,但是对于输出到控制器的日志,只需要输出info级别的就可以了,这样可以大大减少平时不需要的一些日志信息。
到这里logging模块对我们来说已经够用了,还有一些日志配置相关的内容基本上可以不用关注,本期结束!