Python Logging 模块
Python 中的 logging 模块可以让你跟踪代码运行时的事件,当程序崩溃时可以查看日志并且发现是什么引发了错误。Log 信息有内置的层级——调试(debugging)、信息(informational)、警告(warnings)、错误(error)和严重错误(critical)。你也可以在 logging 中包含 traceback 信息。不管是小项目还是大项目,都推荐在 Python 程序中使用 logging
为什么使用 logging
当你运行一个 Python 脚本时,你可能想要知道脚本的哪个部分在执行,并且检视变量的当前值。
当你运行一个 Python 脚本时,你可能想要知道脚本的哪个部分在执行,并且检视变量的当前值。
通常,可以只使用print()打印出你想要的信息。在小程序中,可能靠这个就足够了。
但问题是,当你处理有很多个模块的大项目时,就需要一个更加灵活的方法。
为什么?
因为代码需要经历开发、调试、审查、测试或者上线等不同阶段。在开发时你想要打印的信息类型可能和上线后你想看到的信息类型完全不同。
也就是说,在“测试”时,你可能只想看警告和错误信息,然而在“调试”时,你可能还想看到跟调试相关的信息。
如果你还想打印出使用的模块以及代码运行的时间,那么你的代码很容易变得混乱。
使用logging模块,这些问题就能很容易地解决。
logging模块可以:
控制信息层级,仅记录需要的信息。
控制显示或者保存日志的时机。
使用内置信息模板控制日志格式。
知晓信息来自于哪个模块。
日志级别
import logging # 引入logging模块 # 将信息打印到控制台上 logging.debug("Santa1") logging.info("Santa2") logging.warning("Santa3") logging.error("Santa4") logging.critical("Santa5") # WARNING:root:Santa3 # ERROR:root:Santa4 # CRITICAL:root:Santa5
上面可以看到只有后面三个能打印出来
默认生成的 root logger 的 level 是 logging.WARNING,低于该级别的就不输出了
级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUG
DEBUG:详细信息,用于诊断问题。Value=10。
INFO:确认代码运行正常。Value=20。
WARNING:意想不到的事情发生了,或预示着某个问题。但软件仍按预期运行。Value=30。
ERROR:出现更严重的问题,软件无法执行某些功能。Value=40。
CRITICAL:严重错误,程序本身可能无法继续运行。Value=50。
这里最高的等级是 CRITICAL 和 FATAL,两个对应的数值都是 50,另外对于 WARNING 还提供了简写形式 WARN,两个对应的数值都是 30
这时候,如果需要显示低于WARNING级别的内容,可以配置 basicConfig 的 level 信息
接下来声明了一个 Logger 对象,它就是日志输出的主类,调用对象的 info() 方法就可以输出 INFO 级别的日志信息,调用 debug() 方法就可以输出 DEBUG 级别的日志信息,非常方便。在初始化的时候我们传入了模块的名称,这里直接使用
__name__
来代替了,就是模块的名称,如果直接运行这个脚本的话就是__main__
,如果是 import 的模块的话就是被引入模块的名称,这个变量在不同的模块中的名字是不同的,所以一般使用__name__
来表示就好了import logging # 引入logging模块 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logging = logging.getLogger(__name__) # 将信息打印到控制台上 logging.debug("Santa1") logging.info("Santa2") logging.warning("Santa3") logging.error("Santa4") logging.critical("Santa5") # 2020-02-28 17:29:34,080 - __main__ - INFO - Santa2 # 2020-02-28 17:29:34,081 - __main__ - WARNING - Santa3 # 2020-02-28 17:29:34,081 - __main__ - ERROR - Santa4 # 2020-02-28 17:29:34,081 - __main__ - CRITICAL - Santa5
另外这里指定了 format 格式的字符串,包括 asctime、name、levelname、message 四个内容,分别代表运行时间、模块名称、日志级别、日志内容,这样输出内容便是这四者组合而成的内容了,这就是 logging 的全局配置
由此可见,相比 print 来说,通过刚才的代码,我们既可以输出时间、模块名称,又可以输出不同级别的日志信息作区分并加以过滤,是不是灵活多了?
当然这只是 logging 模块的一小部分功能,接下来我们首先来全面了解一下 basicConfig 的参数都有哪些:
- filename:即日志输出的文件名,如果指定了这个信息之后,实际上会启用 FileHandler,而不再是 StreamHandler,这样日志信息便会输出到文件中了。
- filemode:这个是指定日志文件的写入方式,有两种形式,一种是 w,一种是 a,分别代表清除后写入和追加写入。
- format:指定日志信息的输出格式,即上文示例所示的参数,部分参数如下所示:
- %(levelno)s:打印日志级别的数值。
- %(levelname)s:打印日志级别的名称。
- %(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]。
- %(filename)s:打印当前执行程序名。
- %(funcName)s:打印日志的当前函数。
- %(lineno)d:打印日志的当前行号。
- %(asctime)s:打印日志的时间。
- %(thread)d:打印线程ID。
- %(threadName)s:打印线程名称。
- %(process)d:打印进程ID。
- %(processName)s:打印线程名称。
- %(module)s:打印模块名称。
- %(message)s:打印日志信息。
- datefmt:指定时间的输出格式。
- style:如果 format 参数指定了,这个参数就可以指定格式化时的占位符风格,如 %、{、$ 等。
- level:指定日志输出的类别,程序会输出大于等于此级别的信息。
- stream:在没有指定 filename 的时候会默认使用 StreamHandler,这时 stream 可以指定初始化的文件流。
- handlers:可以指定日志处理时所使用的 Handlers,必须是可迭代的。
输出到文件
import logging from logging.handlers import HTTPHandler import sys logger = logging.getLogger(__name__) logger.setLevel(level=logging.DEBUG) stream_handler = logging.StreamHandler(sys.stdout) stream_handler.setLevel(level=logging.DEBUG) logger.addHandler(stream_handler) file_handler = logging.FileHandler('output.log') file_handler.setLevel(level=logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.info('This is a log info') logger.debug('Debugging') logger.warning('Warning exists') logger.info('Finish')
捕获 Traceback
如果遇到错误,我们更希望报错时出现的详细 Traceback 信息,便于调试,利用 logging 模块我们可以非常方便地实现这个记录,我们用一个实例来感受一下:
import logging logger = logging.getLogger(__name__) logger.setLevel(level=logging.DEBUG) # Formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # FileHandler file_handler = logging.FileHandler('result.log') file_handler.setFormatter(formatter) logger.addHandler(file_handler) # StreamHandler stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) logger.addHandler(stream_handler) # Log logger.info('Start') logger.warning('Something maybe fail.') try: result = 10 / 0 except Exception: logger.error('Faild to get result', exc_info=True) logger.info('Finished')
这里我们在 error() 方法中添加了一个参数,将 exc_info 设置为了 True,这样我们就可以输出执行过程中的信息了,即完整的 Traceback 信息。