Python Logging 指南

文章翻译自官方文档:Logging HOWTOhtml

基础日志教程

日志记录是一种跟踪某些软件运行时发生的事件的方法。该软件的开发人员将日志记录调用添加到其代码中,以指示已发生某些事件。事件由描述性消息描述,该消息能够可选地包含可变数据(即每次事件发生时可能不一样的数据)。事件也具备开发人员对事件的重要性;重要性也能够称为水平或严重程度。python

什么时候使用日志

Logging 为简单的日志记录使用提供了一组便利功能。它们是 debug(), info(), warning(), error()critical()。要肯定什么时候使用日志记录,请参阅下表,其中列出了针对一组常见任务中的每一个任务的最佳工具。程序员

您要执行的任务 这项任务的最佳工具
显示控制台输出,以便正常使用命令行脚本或程序 print()
报告在程序正常运行期间发生的事件(例如,用于状态监测或故障调查) logging.info()(或者 logging.debug() 用于很是详细的输出以用于诊断目的)
发出有关特定运行时事件的警告 warnings.warn(): 在代码库中,若是问题是能够避免的,则应修改客户端应用程序以消除警告

logging.warning(): 若是客户端应用程序没法处理该状况,但仍应注意该事件
报告有关特定运行时事件的错误 抛出异常
报告在不引起异常的状况下抑制错误(例如,长时间运行的服务器进程中的错误处理程序) logging.error(), logging.exception()logging.critical() 适用于特定错误和应用程序域

日志函数以它们用于跟踪的事件的级别或严重性命名。标准级别及其适用性描述以下(按严重程度递增):api

级别 何时使用
DEBUG 详细信息,一般仅在诊断问题时才有意义。
INFO 确认事情按预期工做。
WARNING 代表发生了意外状况,或代表在不久的未来出现了一些问题(例如 “磁盘空间不足”)。可是该软件仍在按预期工做。
ERROR 因为更严重的问题,该软件没法执行某些功能。
CRITICAL 严重错误,代表程序自己可能没法继续运行。

默认级别为 WARNING ,这意味着将仅跟踪此级别及更高级别的事件,除非日志包已配置为执行其余操做。bash

能够以不一样方式处理被跟踪的事件。处理跟踪事件的最简单方法是将它们打印到控制台。另外一种常见方法是将它们写入磁盘文件。服务器

一个简单的例子

一个很是简单的例子:网络

import logging
logging.warning('Watch out!')  # 将打印消息到控制台
logging.info('I told you so')  # 不会打印任何东西
复制代码

若是您在脚本中输入这几行并运行它,您将看到:app

WARNING:root:Watch out!
复制代码

打印在控制台上。 INFO 消息不会出现,由于默认级别为 WARNING 。打印的消息包括记录调用中提供的事件的级别和描述的指示,即 “Watch out!”。暂时不要担忧 'root' 部分:它将在后面解释。若是须要,能够很是灵活地格式化实际输出;格式化选项也将在稍后解释。模块化

记录到文件

一种很是常见的状况是在文件中记录日志事件,因此让咱们看看下一步。请务必在新启动的 Python 解释器中尝试如下操做,而且不要只继续上述会话:函数

import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
复制代码

如今,若是咱们打开文件并查看咱们的内容,咱们应该找到日志消息:

DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this, too
复制代码

此示例还说明了如何设置做为跟踪阈值的日志记录级别。在这种状况下,由于咱们将阈值设置为 DEBUG ,因此打印了全部消息。

若是要从命令行选项设置日志记录级别,例如:

--log=INFO
复制代码

而且你有一个变量 loglevel--log 传递的参数的值,你可使用:

getattr(logging, loglevel.upper())
复制代码

经过 loglevel 参数获取您将传递给 basicConfig() 的值。您可能还但愿检查用户的输入值,以下例所示:

# 假设 loglevel 是从命令行参数中获取的字符串值。 转换为大写以容许用户
# 指定 --log=DEBUG 或 --log=debug
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level, ...)
复制代码

basicConfig() 的调用应该在调用 debug()info() 等以前进行。因为它是一次性的简单配置工具,只有第一次调用才会真正作事情:后续调用其实是无效的。

若是屡次运行上述脚本,则连续运行的消息将附加到文件 example.log 中。若是您但愿每次运行从新开始,而不记住早期运行的消息,则能够指定 filemode 参数,经过将上例中的调用更改成:

logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)
复制代码

输出将与以前相同,但再也不附加日志文件,所以早期运行的消息将丢失。

多个模块中的日志记录

若是您的程序包含多个模块,这里有一个如何组织日志记录的示例:

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
复制代码
# mylib.py
import logging

def do_something():
    logging.info('Doing something')
复制代码

若是你运行 myapp.py,你应该在 myapp.log 中看到这个:

INFO:root:Started
INFO:root:Doing something
INFO:root:Finished
复制代码

但愿大家能看到。您可使用 mylib.py 中的模式将此归纳为多个模块。请注意,对于这种简单的使用模式,除了查看事件描述以外,仅仅经过查看日志文件,您不会知道您的消息来自应用程序中的何处。若是要跟踪消息的位置,则须要参考教程级别以外的文档 -- 请参阅高级日志教程

记录变量数据

要记录变量数据,请使用格式字符串做为事件描述消息,并将变量数据做为参数附加。例如:

import logging
logging.warning('%s before you %s', 'Look', 'leap!')
复制代码

将显示:

WARNING:root:Look before you leap!
复制代码

如您所见,将可变数据合并到事件描述消息中使用旧的 % 样式字符串格式。这是为了向后兼容:日志包也支持更新的格式化选项,如 str.format()string.Template。但探索它们超出了本教程的范围,相关信息请参阅 -- 在整个应用程序中使用特定格式样式

更改显示消息的格式

要更改用于显示消息的格式,您须要指定要使用的格式:

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should appear on the console')
logging.info('So should this')
logging.warning('And this, too')
复制代码

这会打印:

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this, too
复制代码

请注意,前面示例中出现的 “root” 已消失。对于能够出如今格式字符串中的一整套内容,你能够参考 LogRecord 属性的文档,但为了简单使用,您只须要 levelname(重要性),message(事件描述,包括可变数据),并可能显示事件发生的时间。这将在下一节中介绍。

在消息中显示日期/时间

要显示事件的日期和时间,您能够在格式字符串中放置 %(asctime)s

import logging
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('is when this event was logged.')
复制代码

应该打印这样的东西:

2010-12-12 11:41:42,612 is when this event was logged.
复制代码

日期/时间显示的默认格式(如上所示)相似于 ISO8601 或 RFC 3339。若是您须要更多地控制日期/时间的格式,请为 basicConfig 提供 datefmt 参数,以下例所示:

import logging
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('is when this event was logged.')
复制代码

这会显示以下:

12/12/2010 11:46:36 AM is when this event was logged.
复制代码

datefmt 参数的格式与 time.strftime() 支持的格式相同。

下一步

基本教程到此结束。它应该足以让您启动并运行 logging。日志包提供了更多功能,但为了充分利用它,您须要花费更多的时间来阅读如下部分。若是你准备好了,能够拿一些你最喜欢的饮料继续。

若是您的日志记录需求很简单,那么使用上面的示例将日志记录合并到您本身的脚本中基本就能够了。

还在?您能够继续阅读接下来的几个部分,这些部分提供了比上面基本部分更高级/深刻的教程。以后,您能够查看 Logging Cookbook

高级日志教程

日志库采用模块化方法,并提供几类组件:记录器 (loggers),处理器 (handlers),过滤器 (filters) 和格式化器 (formatters)。

  • 记录器公开应用程序代码直接使用的接口。
  • 处理器将日志(由记录器建立)发送到适当的目标。
  • 过滤器提供了更精细的设施,用于肯定要输出的日志记录。
  • 格式化器指定最终输出中的日志记录的布局。

日志事件信息在 LogRecord 实例中的记录器,处理器,过滤器和格式化器之间传递。

经过在 Logger 类的实例(如下称为记录器)上调用方法来执行日志记录。每一个实例都有一个名称,它们在概念上以点(句点)做为分隔符排列在命名空间层次结构中。例如,名为 “scan” 的记录器是记录器 'scan.text','scan.html' 和 'scan.pdf' 的父级。记录器名称能够是您想要的任何名称,并指明记录消息来源的应用程序区域。

在命名记录器时使用的一个好习惯是在每一个使用日志记录的模块中使用模块级记录器,命名以下:

logger = logging.getLogger(__name__)
复制代码

这意味着记录器名称跟踪包/模块层次结构,而且直观地显示从记录器名称记录事件的位置。

记录器层次结构的根称为根记录器。这是函数 debug()info()warning()error()critical() 使用的记录器,它只调用根记录器的同名方法。函数和方法具备相同的签名。根记录器的名称在记录的输出中打印为 “root”。

固然,能够将消息记录到不一样的目的地。软件包中包含支持,用于将日志消息写入文件,HTTP GET/POST 位置,经过 SMTP 发送电子邮件,通用套接字,队列或特定于操做系统的日志记录机制(如 syslog 或 Windows NT 事件日志)。目标由处理器类提供。若是内置处理器类未知足你的特殊要求,则能够建立本身的日志目标类。

默认状况下,没有为任何日志记录消息设置目标。您可使用 basicConfig() 指定目标(例如控制台或文件),如前文中所示。若是调用函数 debug()info()warning()error()critical(),它们将检查是否没有设置目标;若是未设置,则在委派给根记录器执行实际消息输出以前,他们将设置控制台的目标(sys.stderr)和显示消息的默认格式。

basicConfig() 为消息设置的默认格式为:

severity:logger name:message
复制代码

您能够经过使用 format 关键字参数将格式字符串传递给 basicConfig() 来更改此设置。有关如何构造格式字符串的全部选项,请参阅格式化对象

记录流程

记录器和处理器中的日志事件信息流程以下图所示。

记录器

Logger 对象有三重做业。首先,它们向应用程序代码公开了几种方法,以便应用程序能够在运行时记录消息。其次,记录器对象根据严重性(默认过滤工具)或过滤器对象肯定要处理的日志消息。最后,记录器对象将相关的日志消息传递给全部感兴趣的日志处理器。

记录器对象上使用最普遍的方法分为两类:配置和消息发送。

这些是最多见的配置方法:

  • Logger.setLevel() 指定记录器将处理的日志级别,其中 debug 是最低内置日志级别,critical 是最高内置日志级别。例如,若是日志级别为 INFO,则记录器将仅处理 INFO,WARNING,ERROR 和 CRITICAL 消息,并将忽略 DEBUG 消息。
  • Logger.addHandler()Logger.removeHandler() 从记录器对象中添加和删除处理器对象。
  • Logger.addFilter()Logger.removeFilter() 从记录器对象中添加和删除过滤器对象。

您不须要始终在您建立的每一个记录器上调用这些方法。请参阅本节的最后两段。

配置 logger 对象后,如下方法将建立日志消息:

  • Logger.debug()Logger.info()Logger.warning()Logger.error()Logger.critical() 都建立日志记录,其中包含一条消息和一个与其各自方法名称对应的级别。该消息其实是一个格式字符串,可能包含 %s%d%f 的标准字符串替换语法,依此类推。其他参数是与消息中的替换字段对应的对象列表。关于 **kwargs,日志记录方法仅关注 exc_info 的关键字,并使用它来肯定是否记录异常信息。
  • Logger.exception() 建立相似于 Logger.error() 的日志消息。区别在于 Logger.exception() 与其一块儿转储堆栈跟踪。仅从异常处理程序调用此方法。
  • Logger.log() 将日志级别做为显式参数。对于记录消息而言,这比使用上面列出的日志级别便捷方法要详细一些,但这能够自定义日志级别。

getLogger() 返回对具备指定名称的记录器实例的引用(若是已提供),若是不是则返回 root。名称是以句点分隔的层次结构。对具备相同名称的 getLogger() 的屡次调用将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。例如,给定一个名为 foo 的记录器,名称为 foo.barfoo.bar.bazfoo.bam 的记录器都是 foo 的后代。

记录器具备有效级别的概念。若是未在记录器上显式设置级别,则使用其父级别做为其有效级别。若是父级没有明确的级别设置,则再检查其父级,依此类推 - 搜索全部祖先,直到找到明确设置的级别。根记录器始终设置了显式级别(默认状况下为 WARNING)。在决定是否处理事件时,记录器的有效级别用于肯定事件是否传递给记录器的处理器。

子记录器将消息传播到与其祖先记录器相关联的处理器。所以,没必要为应用程序使用的全部记录器定义和配置处理器。为顶级记录器配置处理器并根据须要建立子记录器就足够了。(可是,您能够经过将记录器的 propagate 属性设置为 False 来关闭传播。)

处理器

处理器对象负责将适当的日志消息(基于日志消息的严重性)分派给处理器的指定目标。Logger 对象可使用 addHandler() 方法向自身添加零个或多个处理器对象。做为示例场景,应用程序可能但愿将全部日志消息发送到日志文件,将错误或更高的全部日志消息发送到标准输出,以及将相当重要的全部消息发送到电子邮箱。此方案须要三个单独的处理器,其中每一个处理器负责将特定严重性的消息发送到特定位置。

标准库包含不少处理器类型(请参阅经常使用处理器);这些教程在其示例中主要使用 StreamHandlerFileHandler

处理器中不多有方法须要应用程序开发人员关注。与使用内置处理器对象(即不建立自定义处理器)的应用程序开发人员相关的惟一处理器方法是如下配置方法:

  • 与记录器对象同样,setLevel() 方法指定将分派到适当目标的最低严重性。为何有两个 setLevel() 方法?记录器中设置的级别肯定将传递给其处理器的消息的严重性。而每一个处理器中设置的级别肯定处理器将发送哪些消息。
  • setFormatter() 选择要使用的此处理器的 Formatter 对象。
  • addFilter()removeFilter() 分别在处理器上配置和取消配置过滤器对象。

应用程序代码不该直接实例化和使用 Handler 的实例。相反,Handler 类是一个基类,它定义了全部处理程序应具备的接口,并创建了子类可使用(或覆盖)的一些默认行为。

格式化器

Formatter 对象配置日志消息的最终顺序,结构和内容。与基本 logging.Handler 类不一样,应用程序代码能够实例化 formatter 类,但若是应用程序须要特殊行为,则可能会对 formatter 进行子类化。构造函数有三个可选参数 - 消息格式字符串,日期格式字符串和样式指示符。

logging.Formatter.__init__(fmt=None, datefmt=None, style='%')

若是没有消息格式字符串,则默认使用原始消息。若是没有日期格式字符串,则默认日期格式为:

%Y-%m-%d %H:%M:%S
复制代码

最后加上毫秒数。样式是 %'{''$' 之一。若是未指定其中一个,则使用 '%'

若是 style'%',则消息格式字符串使用 %(<dictionary key>)s 样式字符串替换;LogRecord 属性中记录了可能的键。若是 style 为 “{”,则假定消息格式字符串与 str.format()(使用关键字参数)兼容,而若是 style 为 “$”,则消息格式字符串应符合 string.Template.substitute() 的预期。

Python 3.2 中添加了 style 参数。

如下消息格式字符串将按如下顺序以人类可读的格式记录时间,消息的严重性和消息的内容:

'%(asctime)s - %(levelname)s - %(message)s'
复制代码

Formatters 使用用户可配置的函数将记录的建立时间转换为元组。默认状况下,使用 time.localtime();要为特定格式化器实例更改此值,请将实例的 converter 属性设置为与 time.localtime()time.gmtime() 具备相同签名的函数。要为全部格式化程序更改它,例如,若是要在 GMT 中显示全部记录时间,请在 Formatter 类中设置 converter 属性(用 GMT 则显示 time.gmtime)。

配置日志记录

程序员能够经过三种方式配置日志记录:

  1. 调用上面列出的配置方法显式建立记录器,处理器和格式化器。
  2. 建立日志配置文件并使用 fileConfig() 函数读取它。
  3. 建立配置信息字典并将其传递给 dictConfig() 函数。

有关最后两个选项的参考文档,请参阅 配置函数。如下示例使用 Python 代码配置一个很是简单的记录器,一个控制台处理器和一个简单的格式化器:

import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# 建立控制台处理器并设置级别进行调试
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
复制代码

从命令行运行此模块将生成如下输出:

$ python simple_logging_module.py
2005-03-19 15:10:26,618 - simple_example - DEBUG - debug message
2005-03-19 15:10:26,620 - simple_example - INFO - info message
2005-03-19 15:10:26,695 - simple_example - WARNING - warn message
2005-03-19 15:10:26,697 - simple_example - ERROR - error message
2005-03-19 15:10:26,773 - simple_example - CRITICAL - critical message
复制代码

如下 Python 模块建立的记录器,处理器和格式化器与上面列出的示例几乎彻底相同,惟一的区别是对象的名称:

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger = logging.getLogger('simpleExample')

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
复制代码

这是 logging.conf 文件:

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
复制代码

输出几乎与基于非配置文件的示例相同:

$ python simple_logging_config.py
2005-03-19 15:38:55,977 - simpleExample - DEBUG - debug message
2005-03-19 15:38:55,979 - simpleExample - INFO - info message
2005-03-19 15:38:56,054 - simpleExample - WARNING - warn message
2005-03-19 15:38:56,055 - simpleExample - ERROR - error message
2005-03-19 15:38:56,130 - simpleExample - CRITICAL - critical message
复制代码

您能够看到配置文件方法与 Python 代码方法相比具备一些优点,主要是配置和代码的分离以及非编码器轻松修改日志记录属性的能力。

!> 警告:fileConfig() 函数采用默认参数 disable_existing_loggers ,出于向后兼容的缘由,默认参数为 True。这多是您想要的,也可能不是,由于它会致使在 fileConfig() 调用以前存在的任何记录器被禁用,除非它们(或祖先)在配置中明确命名。有关详细信息,请参阅参考文档,若是须要,请为此参数指定 False

传递给dictConfig() 的字典也可使用键 disable_existing_loggers 指定一个布尔值,若是未在字典中明确指定,则默认状况下将其解释为 True。这会致使上面描述的记录器禁用行为,这可能不是您想要的 - 在这种状况下,请显式提供值为 False 的键。

请注意,配置文件中引用的类名称须要相对于日志记录模块,或者可使用常规导入机制解析的绝对值。所以,您可使用 WatchedFileHandler(相对于日志记录模块)或 mypackage.mymodule.MyHandler(对于在 mypackage 包和模块 mymodule 中定义的类,其中 mypackage 在 Python 导入路径上可用)

在 Python 3.2 中,引入了一种新的配置日志记录的方法,使用字典来保存配置信息。这提供了上面概述的基于配置文件的方法的功能的超集,而且是新应用程序和部署的推荐配置方法。由于 Python 字典用于保存配置信息,而且因为您可使用不一样的方式填充该字典,所以您有更多的配置选项。例如,您可使用 JSON 格式的配置文件,或者,若是您有权访问 YAML 处理函数,则可使用 YAML 格式的文件来填充配置字典。或者,固然,您能够在 Python 代码中构建字典,经过套接字以序列化形式接收它,或者使用对您的应用程序有意义的任何方法。

如下是与上述相同配置的示例,采用YAML格式,用于新的基于字典的方法:

version: 1
formatters:
  simple:
    format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout
loggers:
  simpleExample:
    level: DEBUG
    handlers: [console]
    propagate: no
root:
  level: DEBUG
  handlers: [console]
复制代码

有关使用字典进行日志记录的详细信息,请参阅 配置函数

若是没有提供配置会发生什么

若是未提供日志记录配置,则可能出现须要输出日志记录事件但没法找到输出事件的处理器的状况。在这些状况下,日志包的行为取决于 Python 版本。

对于 3.2 以前的 Python 版本,行为以下:

  • 若是 logging.raiseExceptionsFalse(生产模式),则会以静默方式删除该事件。
  • 若是 logging.raiseExceptionsTrue(开发模式),则会打印一条消息 “没法找到记录器 X.Y.Z 的处理器”。

在 Python 3.2 及更高版本中,行为以下:

  • 该事件使用 “最后的处理器” 输出,存储在 logging.lastResort 中。此内部处理器不与任何记录器关联,而且像 StreamHandler 同样将事件描述消息写入 sys.stderr 的当前值(所以尊重可能有效的任何重定向)。没有对消息进行格式化 - 只打印裸事件描述消息。处理程序的级别设置为 WARNING,所以将输出此级别和更高级别的全部事件。

要获取 3.2 以前的行为,logging.lastResort 能够设置为 None

为库配置日志记录

在开发使用日志记录的库时,您应该注意记录库如何使用日志记录 - 例如,使用的记录器的名称。还须要考虑其日志记录配置。若是应用程序不使用日志记录,而且库代码进行日志记录调用,则(如上一节所述)严重性为 WARNING 和更高的事件将打印到 sys.stderr。这被认为是最好的默认行为。

若是因为某种缘由您不但愿在没有任何日志记录配置的状况下打印这些消息,则能够将无操做处理器附加到库的顶级记录器。这样能够避免打印消息,由于将始终为库的事件找处处理器:它不会产生任何输出。若是库用户配置日志以供应用程序使用,多是配置将添加一些处理器,若是级别已适当配置,则在库代码中进行的日志记录调用将正常地将输出发送给这些处理器。

日志包中包含一个什么都不作的处理器:NullHandler(自 Python 3.1 起)。能够将此处理程序的实例添加到库使用的日志记录命名空间的顶级记录器中(若是要在没有日志记录配置的状况下阻止将库的记录事件输出到 sys.stderr)。若是库 foo 的全部日志记录都是使用名称匹配 'foo.x''foo.x.y' 等的记录器完成的,那么代码:

import logging
logging.getLogger('foo').addHandler(logging.NullHandler())
复制代码

应该有所指望的效果。若是组织生成了许多库,则指定的记录器名称能够是 “orgname.foo” 而不只仅是 “foo”。

?> 注意:强烈建议您不要将 NullHandler 之外的任何处理程序添加到库的记录器中。这是由于处理器的配置是使用您的库的应用程序开发人员的特权。用程序开发人员了解他们的目标受众以及哪些处理器最适合他们的应用程序:若是你在引擎盖下添加处理器,你可能会干扰他们执行单元测试和提供符合他们要求的日志的能力。

记录级别

日志记录级别的数值在下表中给出。若是要定义本身的级别,而且须要它们具备相对于预约义级别的特定值,则主要关注这些级别。若是您使用相同的数值定义级别,它将覆盖预约义的值;预约义的名称将丢失。

级别 数值
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 0

级别也能够与记录器相关联,由开发人员或经过加载已保存的日志记录配置来设置。在记录器上调用日志记录方法时,记录器会将其自身级别与方法调用关联的级别进行比较。若是记录器的级别高于方法调用的级别,则实际上不会生成任何记录消息。这是控制日志输出详细程度的基本机制。

记录消息被编码为 LogRecord 类的实例。当记录器决定实际记录事件时,将从记录消息建立 LogRecord 实例。

记录消息经过使用处理器来处理调度机制,处理器是 Handler 类的子类的实例。处理器负责确保记录的消息(以 LogRecord 的形式)最终位于特定位置(或一组位置),这对于该消息的目标受众是有用的(例如最终用户,支持服务台员工,系统管理员,开发人员)。处理器传递用于特定目标的 LogRecord 实例。每一个记录器能够有零个,一个或多个与之关联的处理器(经过 LoggeraddHandler() 方法)。除了与记录器直接关联的任何处理器以外,还会调用与记录器的全部祖先关联的全部处理器来分派消息(除非记录器的 propagate 标志设置为 False 值,此时传递给祖先处理程序中止)。

就像记录器同样,处理器能够具备与它们相关联的级别。处理器的级别充当过滤器,其方式与记录器级别相同。若是处理器决定实际调度事件,则使用 emit() 方法将消息发送到其目标。大多数用户定义的 Handler 子类都须要覆盖此 emit()

自定义级别

定义您本身的级别是能够的,但不必定是必要的,由于现有级别是根据实践经验选择的。可是,若是您确信须要自定义级别,则在执行此操做时应特别当心,若是您正在开发库,则定义自定义级别多是一个很是糟糕的主意。那是由于若是多个库做者都定义了他们本身的自定义级别,因为给定的数值对于不一样的库而言可能意味着不一样的事物,所以有可能使用这些多个库的日志输出对于使用开发者来讲难以控制和(或)解释。

经常使用处理器

除了基本的 Handler 类以外,还提供了许多有用的子类:

  1. StreamHandler 实例将消息发送到流(类文件对象)。
  2. FileHandler 实例将消息发送到磁盘文件。
  3. BaseRotatingHandler 是在某个点切割日志文件的处理器的基类。它并不意味着直接实例化。而是使用 RotatingFileHandlerTimedRotatingFileHandler
  4. RotatingFileHandler 实例将消息发送到磁盘文件,支持最大日志文件大小和日志文件切割。
  5. TimedRotatingFileHandler 实例将消息发送到磁盘文件,以特定的时间间隔切割日志文件。
  6. SocketHandler 实例将消息发送到 TCP/IP 套接字。从 3.4 开始,也支持 Unix 域套接字。
  7. DatagramHandler 实例将消息发送到 UDP 套接字。从 3.4 开始,也支持 Unix 域套接字。
  8. SMTPHandler 实例将消息发送到指定的电子邮件地址。
  9. SysLogHandler 实例将消息发送到 Unix syslog 守护程序,能够是在远程计算机上。
  10. NTEventLogHandler 实例将消息发送到 Windows NT/2000/XP 事件日志。
  11. MemoryHandler 实例将消息发送到内存中的缓冲区,只要知足特定条件,就会刷新内存中的缓冲区。
  12. HTTPHandler 实例使用 GET 或 POST 语义将消息发送到 HTTP 服务器。
  13. WatchedFileHandler 实例监视他们要记录的文件。若是文件发生更改,则会关闭该文件并使用文件名从新打开。此处理程序仅在类 Unix 系统上有用; Windows 不支持使用的基础机制。
  14. QueueHandler 实例将消息发送到队列,例如队列或多处理模块中实现的队列。
  15. NullHandler 实例不会对错误消息执行任何操做。

NullHandlerStreamHandlerFileHandler 类在核心日志包中定义。其余处理程序在子模块 logging.handlers 中定义。(还有另外一个子模块 logging.config,用于配置功能。)

记录的消息被格式化以便经过 Formatter 类的实例进行呈现。它们使用适合与 % 运算符和字典一块儿使用的格式字符串进行初始化。

对于批量格式化多个消息,可使用 BufferingFormatter 的实例。除了格式字符串(应用于批处理中的每一个消息)以外,还提供了标题和尾部格式字符串。

当基于记录器级别和(或)处理器级别的过滤不够时,能够将过滤器的实例添加到 LoggerHandler 实例(经过他们的 addFilter() 方法)。在决定进一步处理消息以前,记录器和处理器都会查询其全部过滤器以获取权限。若是任何过滤器返回 false 值,则不会进一步处理该消息。

基本的过滤器功能容许按特定的记录器名称进行过滤。若是使用此功能,则容许经过过滤器发送到指定记录器及其子项的消息,并删除全部其余消息。

记录期间引起的异常

日志包旨在吞噬登陆生产时发生的异常。这样能够在处理日志记录事件时发生错误 - 例如记录错误配置,网络或其余相似错误 - 不要致使使用日志记录的应用程序过早终止。

永远不会吞下 SystemExitKeyboardInterrupt 异常。在 Handler 子类的 emit() 方法期间发生的其余异常将传递给其 handleError() 方法。

HandlerhandleError() 的默认实现检查是否设置了模块级变量 raiseExceptions。若是设置,则会向 sys.stderr 打印回溯。若是未设置,则吞下异常。

?> 注意:raiseExceptions 的默认值为 True。这是由于在开发期间,您一般但愿收到发生的任何异常的通知。建议您将生产使用的 raiseExceptions 设置为 False

使用任意对象做为消息

在前面的部分和示例中,假设记录事件时传递的消息是字符串。可是,这不是惟一的可能性。您能够将任意对象做为消息传递,而且当日志记录系统须要将其转换为字符串表示时,将调用其 __str__() 方法。实际上,若是您愿意,能够避免彻底计算字符串表示 - 例如, SocketHandler 经过序列化并经过线路发送事件来发出事件。

相关文章
相关标签/搜索