python 日志_日志 HOWTO

进阶日志教程¶

日志库采用模块化方法,并提供几类组件:记录器、处理程序、过滤器和格式化程序。

记录器暴露了应用程序代码直接使用的接口。

处理程序将日志记录(由记录器创建)发送到适当的目标。

过滤器提供了更精细的附加功能,用于确定要输出的日志记录。

格式化程序指定最终输出中日志记录的样式。

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

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

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

logger = logging.getLogger(__name__)

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

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

当然,可以将消息记录到不同的地方。 软件包中的支持包含,用于将日志消息写入文件、 HTTP GET/POST 位置、通过 SMTP 发送电子邮件、通用套接字、队列或特定于操作系统的日志记录机制(如 syslog 或 Windows NT 事件日志)。 目标由 handler 类提供。 如果你有任何内置处理程序类未满足的特殊要求,则可以创建自己的日志目标类。

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

severity:logger name:message

你可以通过使用 format 参数将格式字符串传递给 basicConfig() 来更改此设置。有关如何构造格式字符串的所有选项,请参阅 格式器对象 。

记录流程¶

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

记录器¶

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

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

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

Logger.setLevel() 指定记录器将处理的最低严重性日志消息,其中 debug 是最低内置严重性级别, critical 是最高内置严重性级别。 例如,如果严重性级别为 INFO ,则记录器将仅处理 INFO 、 WARNING 、 ERROR 和 CRITICAL 消息,并将忽略 DEBUG 消息。

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

配置记录器对象后,以下方法将创建日志消息:

Logger.debug() 、 Logger.info() 、 Logger.warning() 、 Logger.error() 和 Logger.critical() 都创建日志记录,包含消息和与其各自方法名称对应的级别。该消息实际上是一个格式化字符串,它可能包含标题字符串替换语法 %s 、 %d 、 %f 等等。其余参数是与消息中的替换字段对应的对象列表。关于 **kwargs ,日志记录方法只关注 exc_info 的关键字,并用它来确定是否记录异常信息。

Logger.log() 将日志级别作为显式参数。对于记录消息而言,这比使用上面列出的日志级别方便方法更加冗长,但这是自定义日志级别的方法。

getLogger() 返回对具有指定名称的记录器实例的引用(如果已提供),或者如果没有则返回 root 。名称是以句点分隔的层次结构。多次调用 getLogger() 具有相同的名称将返回对同一记录器对象的引用。在分层列表中较低的记录器是列表中较高的记录器的子项。例如,给定一个名为 foo 的记录器,名称为 foo.bar 、 foo.bar.baz 和 foo.bam 的记录器都是 foo 子项。

记录器具有 有效等级 的概念。如果未在记录器上显式设置级别,则使用其父级别作为其有效级别。如果父级没有明确的级别设置,则检查 其 父级。依此类推,搜索所有上级元素,直到找到明确设置的级别。根记录器始终具有显式级别集(默认情况下为 WARNING )。在决定是否处理事件时,记录器的有效级别用于确定事件是否传递给记录器的处理程序。

子记录器将消息传播到与其上级记录器关联的处理程序。因此,不必为应用程序使用的所有记录器定义和配置处理程序。为顶级记录器配置处理程序并根据需要创建子记录器就足够了。(但是,你可以通过将记录器的 propagate 属性设置 False 来关闭传播。)

处理程序¶

Handler 对象负责将适当的日志消息(基于日志消息的严重性)分派给处理程序的指定目标。 Logger 对象可以使用 addHandler() 方法向自己添加零个或多个处理程序对象。作为示例场景,应用程序可能希望将所有日志消息发送到日志文件,将错误或更高的所有日志消息发送到标准输出,以及将所有关键消息发送至一个邮件地址。 此方案需要三个单独的处理程序,其中每个处理程序负责将特定严重性的消息发送到特定位置。

处理程序中很少有方法可供应用程序开发人员使用。与使用内置处理程序对象(即不创建自定义处理程序)的应用程序开发人员相关的唯一处理程序方法是以下配置方法:

setLevel() 方法,就像在记录器对象中一样,指定将被分派到适当目标的最低严重性。为什么有两个 setLevel() 方法?记录器中设置的级别确定将传递给其处理程序的消息的严重性。每个处理程序中设置的级别确定处理程序将发送哪些消息。

setFormatter() 选择一个该处理程序使用的 Formatter 对象。

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

格式化程序¶

格式化程序对象配置日志消息的最终顺序、结构和内容。 与 logging.Handler 类不同,应用程序代码可以实例化格式化程序类,但如果应用程序需要特殊行为,则可能会对格式化程序进行子类化。构造函数有三个可选参数 —— 消息格式字符串、日期格式字符串和样式指示符。

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

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

%Y-%m-%d %H:%M:%S

最后加上毫秒数。 style 是 %,'{ ' 或 '$' 之一。 如果未指定其中一个,则将使用 '%'。

如果 style 是 '%',则消息格式字符串使用 %()s 样式字符串替换;可能的键值在 LogRecord 属性 中。 如果样式为 '{',则假定消息格式字符串与 str.format() (使用关键字参数)兼容,而如果样式为 '$' ,则消息格式字符串应符合 string.Template.substitute() 。

在 3.2 版更改:添加 style 形参。

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

'%(asctime)s-%(levelname)s-%(message)s'

格式化程序使用用户可配置的函数将记录的创建时间转换为元组。 默认情况下,使用 time.localtime() ;要为特定格式化程序实例更改此项,请将实例的 converter 属性设置为具有相同签名的函数 time.localtime() 或 time.gmtime() 。 要为所有格式化程序更改它,例如,如果你希望所有记录时间都以 GMT 显示,请在格式化程序类中设置 converter 属性(对于 GMT 显示,设置为 time.gmtime )。

配置日志记录¶

开发者可以通过三种方式配置日志记录:

使用调用上面列出的配置方法的 Python 代码显式创建记录器、处理程序和格式化程序。

创建日志配置文件并使用 fileConfig() 函数读取它。

创建配置信息字典并将其传递给 dictConfig() 函数。

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

import logging

# create logger

logger = logging.getLogger('simple_example')

logger.setLevel(logging.DEBUG)

# create console handler and set level to 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.warning('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.warning('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() 调用之前存在的任何非 root 记录器被禁用。有关更多信息,请参阅参考文档,如果需要,请为此参数指定 False 。

传递给 dictConfig() 的字典也可以用键 disable_existing_loggers 指定一个布尔值,如果没有在字典中明确指定,也默认被解释为 True 。这会导致上面描述的记录器禁用行为,这可能不是你想要的——在这种情况下,明确地为键提供 False 值。

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

在 Python 3.2 中,引入了一种新的配置日志记录的方法,使用字典来保存配置信息。 这提供了上述基于配置文件方法的功能的超集,并且是新应用程序和部署的推荐配置方法。 因为 Python 字典用于保存配置信息,并且由于你可以使用不同的方式填充该字典,因此你有更多的配置选项。 例如,你可以使用 JSON 格式的配置文件,或者如果你有权访问 YAML 处理功能,则可以使用 YAML 格式的文件来填充配置字典。当然,你可以在 Python 代码中构造字典,通过套接字以 pickle 形式接收它,或者使用对你的应用程序合理的任何方法。

以下是与上述相同配置的示例,采用 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.raiseExceptions 为 False (生产模式),则会以静默方式丢弃该事件。

如果 logging.raiseExceptions 为 True (开发模式),则会打印一条消息“无法找到记录器 X.Y.Z 的处理程序”。

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

事件使用“最后的处理程序”输出,存储在 logging.lastResort 中。 这个内部处理程序与任何记录器都没有关联,它的作用类似于 StreamHandler ,它将事件描述消息写入 sys.stderr 的当前值(因此服从任何可能的重定向影响)。 没有对消息进行格式化——只打印裸事件描述消息。处理程序的级别设置为“警告”,因此将输出此级别和更高级别的所有事件。

要获得 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 以外的任何处理程序添加到库的记录器中 。这是因为处理程序的配置是使用你的库的应用程序开发人员的权利。应用程序开发人员了解他们的目标受众以及哪些处理程序最适合他们的应用程序:如果你在“底层”添加处理程序,则可能会干扰他们执行单元测试和提供符合其要求的日志的能力。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值