python logging()
如何在Python编程中记录消息?有哪些不同的python日志记录级别?我可以在日志消息中添加一些自定义格式吗?如何将日志输出重定向到文件?
日志通常是存储消息的系统文件,通常会插入有用的信息,例如时间戳或者运行程序的用户名。通常,日志每天轮换(重命名)并压缩;这样一来,它们不会填满磁盘并自己造成问题。当程序出现问题时,我们可以查看相应的日志文件以查看发生了什么。异常的内容在日志中特别有用,因为它们向我们显示程序崩溃的实际行以及原因。
日志记录是任何代码或者脚本中最关键的部分之一,它可以帮助开发人员调试任何类型的故障。因此,始终建议在代码中添加足够的日志消息。
我已经简要记下了使用python日志记录模块所涉及的步骤。 "我们急吗?",只是在寻找快速步骤来为python脚本提供基本的日志记录配置,请跳至以下内部超链接:
导入logging模块
使用logging.getLogger()定义记录器名称
配置logging.basicConfig
使用日志记录模块的Python脚本示例
如果我们仍然其中我建议阅读全文,以获取有关Python日志记录模块的正确概述。在python中,我们具有内置的日志记录模块,可用于生成不同级别的日志。在本教程中,我将介绍以下主题:
logging模块简介
python日志记录模块中可用的不同类
使用处理程序
使用
Formatter
格式化日志消息的输出Python日志记录级别
记录到日志文件,控制台和系统日志
使用
basicConfig
,dictConfig
,fileConfig
配置日志记录
python日志记录模块概述
Python有一个内置的日志记录模块,该模块非常有用,但是很难正确使用。结果通常是人们只是完全禁用日志记录,而是使用打印语句。这是有见地的,但浪费了Python中非常广泛的日志记录系统。由标准库模块提供的日志记录API的主要好处是所有Python模块都可以参与日志记录,因此应用程序日志可以包含我们自己的消息以及与第三方模块的消息集成的消息。
日志记录模块的最重要的对象如下:
Logger:
实际的日志记录界面Handler
:处理日志语句并输出Formatter:
将输入数据格式化为字符串Filter:
这允许过滤某些消息
当我们有print()函数时,为什么要使用日志记录?
绝对取决于我们希望在Python脚本中具有哪种行为。
但是我们必须了解
print()
与logging
相比的好处非常有限。使用
print()
,我们几乎可以将消息放在STDOUT上,而没有关于消息类型的其他信息
使用logging
可以:
为消息设置不同的级别,例如
info()
,debug()
,error()
,warning()
添加消息的不同格式,即放置日期时间戳记,文件名,进程,路径和更多" LogRecord"属性
记录到文件,套接字,几乎所有内容,同时全部。
因此,与print()
函数相比,当我们使用logging()
时,我们对消息有更多的控制权。
Python日志记录级别
根据需要在Python程序中跟踪的消息或者事件的严重性,可以使用不同的预定义级别。
级别 | 数值 | 何时使用? |
---|---|---|
CRITICAL | 50 | 严重错误,表示程序本身可能无法继续运行。 |
错误 | 40 | 由于更严重的问题,软件无法执行某些功能。 |
警告 | 30 | 表示发生了意外情况,或者表示不久的将来出现了问题(例如“磁盘空间不足”)。软件仍按预期工作。 |
信息 | 20 | 确认一切正常。 |
调试 | 10 | 详细信息,通常只有在诊断问题时才感兴趣。 |
NOTSET | 0 | 创建记录器时,级别设置为NOTSET |
我们还可以选择创建自己的自定义级别
同样,在定义自定义级别时,如果它们具有相同的数值,则必须覆盖现有级别。
数字是这些日志级别的数值。虽然我们通常可以忽略它们,但是在设置最低级别时,"顺序显然很重要"
我们需要在脚本中定义日志记录级别。默认级别为" WARNING",这意味着将仅跟踪此级别及更高级别的事件,除非日志记录程序包配置为执行其他操作。
我们必须知道定义日志记录级别时要考虑的顺序。假设我们将日志记录级别定义为
logging.WARNING
,在这种情况下,将允许我们定义warning()
,error()
和critical()
级别。
例如,在此python脚本中,我刚刚导入了日志记录模块,但尚未定义任何级别
#!/usr/bin/env python3 import logging logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical')
当我们执行该脚本时,仅获得警告(warning()),错误(error)和临界(critical)的输出。这是因为默认的日志记录级别设置为warning()
,并且脚本将仅考虑所有等于或者大于warning()
的数值。
# python3 /tmp/logging_ex.py WARNING:root:warning ERROR:root:error CRITICAL:root:critical
最低级别为DEBUG,最高级别为CRITICAL。因此建议我们始终使用最小的值(即DEBUG)定义日志记录级别,以便在需要时也可以使用更高的日志记录级别。
在接下来的章节中,我们将学习有关如何设置日志记录级别的更多信息
使用logging.getLogger()定义记录器名称
"我们观察到了吗?"在上面的示例中,我们在日志记录输出中扎根了吗?
记录器层次结构的"根"称为"根记录器"。
这就是功能
debug()
,info()
,warning()
,error()
和critical()
所使用的记录器,它们仅调用根记录器的同名方法。根记录器的名称在记录的输出中显示为" root"。
以下函数用于检索或者创建新的日志记录对象:
getLogger(logname)
例如:
#!/usr/bin/env python3 import logging # Get the default logger object print(logging.getLogger())
这将打印默认的记录器名称和级别:
# python3 /tmp/logging_ex.py <RootLogger root (WARNING)>
我们可以使用相同的函数定义自定义记录器名称,例如:
logger = logging.getLogger(__name__)
这将创建一个模块范围的记录器对象,其名称与模块名称匹配。 " root logger"的名称为"";也就是一个空字符串。
使用logging.getLogger()
命名我们的Logger的四种常见用例
模块名称
对象实例
类名
功能名称
使用basicConfig()配置日志记录
使用basicConfig()可以快速方便地开始记录配置。
如果我们只需要快速记录正在编写的脚本,而不需要完整的应用程序,则建议使用此方法。
logging.basicConfig()
通过创建带有默认Formatter
的StreamHandler
并将其添加到root logger
中,为记录系统进行基本配置。如果没有为根记录器定义处理程序,则
debug()
,info()
,warning()
,error()
和critical()
函数将自动调用basicConfig()
。我们检查check官方docs.python.org以获得
basicConfig()
支持的关键字参数列表。
带有basicConfig()的Python脚本示例
在此示例中,我将对以下关键字使用logging.basicConfig()
:
filename:
指定使用指定的文件名而不是StreamHandler
创建FileHandler
。format:为处理程序使用指定的格式字符串。
级别:将根记录器级别设置为指定级别。
#!/usr/bin/env python3 import logging log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') logging.basicConfig( level=logging.DEBUG, format=log_format, filename=('debug.log'), ) logging.debug('debug') logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical')
执行脚本
# python3 /tmp/logging_ex.py
执行脚本后检查debug.log文件
# tail -f debug.log [2017-06-24 09:19:58,490] DEBUG root debug [2017-06-24 09:19:58,490] INFO root info [2017-06-24 09:19:58,490] WARNING root warning [2017-06-24 09:19:58,491] ERROR root error [2017-06-24 09:19:58,491] CRITICAL root critical
但是,我们再次看到根记录器的名称,因此我们将"定义我们自己的自定义记录器名称"。
#!/usr/bin/env python3 import logging log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') # Define basic configuration logging.basicConfig( # Define logging level level=logging.DEBUG, # Define the format of log messages format=log_format, # Provide the filename to store the log messages filename=('debug.log'), ) # Define your own logger name logger = logging.getLogger("my_logger") # Now we use our logger object instead of logging logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
执行脚本
# python3 /tmp/logging_ex.py
执行脚本后检查debug.log
文件,现在我们获得了自定义的记录器名称
[2017-06-24 10:13:43,651] DEBUG my_logger debug [2017-06-24 10:13:43,651] INFO my_logger info [2017-06-24 10:13:43,651] WARNING my_logger warning [2017-06-24 10:13:43,651] ERROR my_logger error [2017-06-24 10:13:43,651] CRITICAL my_logger critical
Python logging.handlers
现在我们知道了设置python日志记录的基础知识,现在让我们了解处理程序。
处理程序对象负责将适当的日志消息(基于日志消息的严重性)调度到处理程序的指定目标
logging.handlers模块提供了大量用于路由,打印或者保存日志消息序列的处理程序。
logging.handlers部分有各种类型的处理程序,本教程在示例中主要使用
StreamHandler
和FileHandler
。
Python脚本示例:仅在控制台上打印消息
在这个例子中,我们将使用
logging.StreamHandler
。e StreamHandler用于写入控制台,如果指定了stream,则实例将使用它来记录输出。否则,将使用
sys.stderr
。我们可以使用
logging.StreamHandler(sys.stdout)
或者只使用logging.StreamHandler()
来使用sys.stderr
。在这两种情况下,消息都将打印在控制台上
#!/usr/bin/env python3 import logging import sys # Define the log format log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') # Define basic configuration logging.basicConfig( # Define logging level level=logging.DEBUG, # Declare the object we created to format the log messages format=log_format, # Declare handlers handlers=[ logging.StreamHandler() ] ) # Define your own logger name logger = logging.getLogger("my_logger") # Now we use our logger object instead of logging logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
接下来执行脚本,这将在控制台上显示消息:
# python3 /tmp/logging_ex.py [2017-06-24 14:28:06,614] DEBUG my_logger debug [2017-06-24 14:28:06,614] INFO my_logger info [2017-06-24 14:28:06,614] WARNING my_logger warning [2017-06-24 14:28:06,614] ERROR my_logger error [2017-06-24 14:28:06,614] CRITICAL my_logger critical
Python脚本示例:仅将消息写入日志文件
对于长时间运行的程序,登录到屏幕不是一个可行的选择。在运行了几个小时的代码之后,最早的日志消息将丢失,即使它们仍然可用,阅读所有日志或者对其进行搜索也不是那么容易。
将日志保存到文件允许无限长(在磁盘允许的范围内),并且可以使用grep之类的工具进行搜索。
位于核心日志记录程序包中的
FileHandler
类将日志记录输出发送到磁盘文件。它继承了StreamHandler的输出功能。
在此脚本中,我们会将消息写入定义为日志文件对象的/tmp/debug.log
中。
#!/usr/bin/env python3 import logging # Log file location logfile = '/tmp/debug.log' # Define the log format log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') # Define basic configuration logging.basicConfig( # Define logging level level=logging.DEBUG, # Declare the object we created to format the log messages format=log_format, # Declare handlers handlers=[ logging.FileHandler(logfile) ] ) # Define your own logger name logger = logging.getLogger("my_logger") # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
执行此脚本
# python3 /tmp/logging_ex.py
这应该创建一个新的日志文件(如果尚不存在)并添加记录器消息
# tail -f /tmp/debug.log [2017-06-24 16:16:17,414] DEBUG my_logger debug [2017-06-24 16:16:17,415] INFO my_logger info [2017-06-24 16:16:17,415] WARNING my_logger warning [2017-06-24 16:16:17,415] ERROR my_logger error [2017-06-24 16:16:17,415] CRITICAL my_logger critical
Python脚本示例:将消息同时写入控制台和日志文件
我们分别了解了两个不同的logging.handlers()
。我们可以将它们结合在一个脚本中,以便能够在控制台终端以及日志文件中写入消息。在此脚本中,我们将在basicConfig()
中使用FileHandler
和Streamhandler
。
#!/usr/bin/env python3 import logging import sys # Log file location logfile = '/tmp/debug.log' # Define the log format log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') # Define basic configuration logging.basicConfig( # Define logging level level=logging.DEBUG, # Declare the object we created to format the log messages format=log_format, # Declare handlers handlers=[ logging.FileHandler(logfile), logging.StreamHandler(sys.stdout), ] ) # Define your own logger name logger = logging.getLogger("my_logger") # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
执行脚本,它应该在控制台上写消息:
# python3 /tmp/logging_ex.py [2017-06-24 16:30:14,163] DEBUG my_logger debug [2017-06-24 16:30:14,163] INFO my_logger info [2017-06-24 16:30:14,163] WARNING my_logger warning [2017-06-24 16:30:14,164] ERROR my_logger error [2017-06-24 16:30:14,164] CRITICAL my_logger critical
同样的内容也将添加到我们的日志文件/tmp/debug.log
中。
[2017-06-24 16:30:14,163] DEBUG my_logger debug [2017-06-24 16:30:14,163] INFO my_logger info [2017-06-24 16:30:14,163] WARNING my_logger warning [2017-06-24 16:30:14,164] ERROR my_logger error [2017-06-24 16:30:14,164] CRITICAL my_logger critical
Python脚本示例:将消息写入syslog(或者rsyslog)
位于logging.handlers模块中的
SysLogHandler
类支持将日志记录消息发送到远程或者本地Unix syslog。为了能够将日志消息发送到syslog,还必须导入
logging.handlers
模块请注意,如果服务器未在UDP端口514上侦听,则" SysLogHandler"可能无法正常工作。
在这种情况下,请检查我们应该为域套接字使用哪个地址-该地址取决于系统。例如,在Linux上通常是"
/dev/log
",而在OS/X上则是"/var/run/syslog
"由于我使用的是Linux环境,因此我将使用/dev/log
#!/usr/bin/env python3 import logging import logging.handlers # Log file location logfile = '/tmp/debug.log' # Define the log format log_format = ( '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') # Define basic configuration logging.basicConfig( # Define logging level level=logging.DEBUG, # Declare the object we created to format the log messages format=log_format, # Declare handlers handlers=[ logging.handlers.SysLogHandler(address="/dev/log"), ] ) # Define your own logger name logger = logging.getLogger("my_logger") # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
接下来执行脚本
# python3 /tmp/logging_ex.py
并观察系统日志(我正在使用journalctl -f
查看最新消息)
Jun 24 16:46:25 client.example.com python3[2467]: [2017-06-24 16:46:25,644] DEBUG my_logger debug Jun 24 16:46:25 client.example.com python3[2467]: [2017-06-24 16:46:25,644] INFO my_logger info Jun 24 16:46:25 client.example.com python3[2467]: [2017-06-24 16:46:25,644] WARNING my_logger warning Jun 24 16:46:25 client.example.com python3[2467]: [2017-06-24 16:46:25,644] ERROR my_logger error Jun 24 16:46:25 client.example.com python3[2467]: [2017-06-24 16:46:25,645] CRITICAL my_logger critical
说明:
我们可以再次结合使用FileHandler(),StreamHandler()和SysLogHandler()来获得所有三种消息,即分别写入文件,在控制台上写入和写入syslog。
创建具有不同日志级别的多个处理程序
我们在上述所有python脚本的
basicConfig()
函数中使用了处理程序。现在让我们在basicConfig()
函数之外使用它。我们可以分配不同的日志文件,日志级别,格式并分配给各个处理程序,并根据要求使用它们。
假设我们希望在控制台上写入的消息格式与我们在日志文件中写入的消息格式不同,因此我们可以创建差异格式并分配给各个处理程序。
同样,我们可以分配不同的日志级别,例如仅在控制台上打印" ERROR"和更高级别的日志,而将" DEBUG"和更高级别的日志写入日志文件。
Python脚本示例:为处理程序分配不同的日志级别和格式
在此示例中,我们创建
console_handler:
在控制台上写消息
使用由print_format对象定义的自己的formatter
设置为" CRITICAL"日志级别,因此控制台上只会打印" CRITICAL"级别的消息
file_handler
将消息写入日志文件(/tmp/debug.log`)
使用由log_format对象定义的自己的formatter
设置为DEBUG日志级别,这样所有具有DEBUG和ad更高级别的消息都将被写入日志文件。
#!/usr/bin/env python3 import logging import sys # Log file location logfile = '/tmp/debug.log' # Define your own logger name logger = logging.getLogger("my_logger") # Set default logging level to DEBUG logger.setLevel(logging.DEBUG) # create a console handler # and define a custom log format, set its log level to CRITICAL print_format = logging.Formatter('%(levelname)-8s %(name)-12s %(message)s') console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.CRITICAL) console_handler.setFormatter(print_format) # create a log file handler # and define a custom log format, set its log level to DEBUG log_format = logging.Formatter('[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s') file_handler = logging.FileHandler(logfile) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(log_format) #Add handlers to the logger logger.addHandler(file_handler) logger.addHandler(console_handler) # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
执行脚本,控制台上的输出仅包含" CRITICAL"消息
# python3 /tmp/logging_ex.py CRITICAL my_logger critical
而日志文件/tmp/debug.log
使用我们定义的日志格式包含了层次结构中DEBUG
之上的所有消息级别
[2017-06-24 19:50:00,433] DEBUG my_logger debug [2017-06-24 19:50:00,433] INFO my_logger info [2017-06-24 19:50:00,434] WARNING my_logger warning [2017-06-24 19:50:00,434] ERROR my_logger error [2017-06-24 19:50:00,434] CRITICAL my_logger critical
使用fileConfig()配置日志记录
有多种方法可以配置日志记录系统,从纯代码到JSON文件,甚至是远程配置。
在上面的示例中,我们使用
basicConfig()
通过带有处理程序的类和函数来配置日志记录。但是正如我们所看到的,我们的最后一个例子对于新手来说太复杂了。现在想象一下,如果我们有更多的处理程序,那么这可能会变得更加复杂。在此示例中,我们将使用
fileConfig()
和日志配置文件来重写上述示例脚本。其中我们将有两个处理程序,即
consoleHandler
将日志写入控制台和fileHandler
将消息写入/tmp/debug.log
仅当日志级别为CRITICAL时,才会写入consoleHandler,而fileHandler将从所有级别写入比DEBUG更高数值的消息
我们将为两个处理程序分配单独的"格式程序"
配置文件格式
该文件必须包含名为[loggers],[handlers]和[formatters]的部分,这些部分按名称标识文件中定义的每种类型的实体。
对于每个这样的实体,都有一个单独的部分来标识该实体的配置方式。
因此,对于[loggers]部分中名为my_logger的记录器,相关的配置详细信息保存在`[logger_my_logger]部分中。
同样,在[handlers]部分中名为
consoleHandler
的处理程序将其配置保存在名为[handler_consoleHandler]的部分中,而在[formatters]部分中名为
fileFormatter
的formatter
将在名为[formatter_fileFormatter]的部分中指定其配置
说明:
尽管键的命名约定并不重要,但定义所有部分很重要。我们可以根据环境分配任何名称。
以下是我们的logging.conf文件。我们可以为该配置文件指定任何名称。
[loggers] keys=root,my_logger [handlers] keys=consoleHandler,fileHandler [formatters] keys=consoleFormatter,fileFormatter [logger_root] level=DEBUG handlers=consoleHandler [logger_my_logger] level=DEBUG handlers=consoleHandler,fileHandler qualname=my_logger propagate=0 [handler_consoleHandler] class=StreamHandler level=CRITICAL formatter=consoleFormatter args=(sys.stdout,) [handler_fileHandler] class=FileHandler level=DEBUG formatter=fileFormatter args=('/tmp/debug.log', 'a') [formatter_consoleFormatter] format=%(levelname)-8s %(name)-12s %(message)s [formatter_fileFormatter] format=[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s
接下来,我们将在python脚本中使用此日志记录配置文件
#!/usr/bin/env python3 import logging import logging.config # Define the logging.conf filePath logging.config.fileConfig('/root/logging.conf', disable_existing_loggers=False) # Define your own logger name logger = logging.getLogger("my_logger") # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
fileConfig()
函数采用默认参数disable_existing_loggers
,出于向后兼容的原因,该参数默认为True
。这可能不是我们想要的,因为它将导致禁用在fileConfig()调用之前存在的所有非root记录器,除非在配置中对其进行了明确命名
接下来执行此脚本并观察输出,我们只会在控制台上收到" CRITICAL"消息
# python3 /tmp/logging_ex.py CRITICAL my_logger critical
虽然我们的日志文件包含从DEBUG级别到更高级别的所有消息
# tail -f /tmp/debug.log [2017-06-24 22:44:18,480] DEBUG my_logger debug [2017-06-24 22:44:18,480] INFO my_logger info [2017-06-24 22:44:18,480] WARNING my_logger warning [2017-06-24 22:44:18,480] ERROR my_logger error [2017-06-24 22:44:18,480] CRITICAL my_logger critical
使用dictConfig()配置日志记录
我们还可以创建一个配置信息字典,并将其传递给dictConfig()函数来配置日志记录
该字典的内容在配置字典架构中描述。
如果在配置过程中遇到错误,则此函数将引发" ValueError"," TypeError"," AttributeError"或者" ImportError",并带有适当的描述性消息。
我们将使用两个带有单独的formatters
和日志级别的处理程序来重用最后一个python脚本示例配置
#!/usr/bin/env python3 import logging import logging.config # Declare handlers, formatters and all functions using dictionary 'key' : 'value' pair logging.config.dictConfig({ 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'consoleFormatter': { 'format': '%(levelname)-8s %(name)-12s %(message)s', }, 'fileFormatter': { 'format': '[%(asctime)s] %(levelname)-8s %(name)-12s %(message)s', }, }, 'handlers': { 'file': { 'filename': '/tmp/debug.log', 'level': 'DEBUG', 'class': 'logging.FileHandler', 'formatter': 'fileFormatter', }, 'console': { 'level': 'CRITICAL', 'class': 'logging.StreamHandler', 'formatter': 'consoleFormatter', }, }, 'loggers': { '': { 'handlers': ['file', 'console'], 'level': 'DEBUG', }, }, }) # Define your own logger name logger = logging.getLogger("my_logger") # Write messages with all different types of levels logger.debug('debug') logger.info('info') logger.warning('warning') logger.error('error') logger.critical('critical')
该脚本的输出仅显示" CRITICAL"级别的消息,如预期的那样
# python3 /tmp/logging_ex.py CRITICAL my_logger critical
而所有DEBUG
和更高级别的消息都使用FileHandler()
登录到/tmp/debug.log
中。
[2017-06-24 23:06:21,195] DEBUG my_logger debug [2017-06-24 23:06:21,195] INFO my_logger info [2017-06-24 23:06:21,195] WARNING my_logger warning [2017-06-24 23:06:21,196] ERROR my_logger error [2017-06-24 23:06:21,196] CRITICAL my_logger critical
提示:
我们还可以使用JSON或者YAML格式来配置日志记录。我将无法解释这些内容,因为本文已经很长了,我可能打算仅针对这些不同的可能选项编写一个单独的教程,以配置带有示例的python日志记录。