logging --- Python 的日志记录工具¶
此模块定义了多个函数和类,为应用程序和库实现了一个灵活的事件日志记录系统。
由标准库模块提供日志记录 API 的主要好处是,所有 Python 模块都可以参与日志记录,因此你的应用程序日志可以包含你自己的消息以及来自第三方模块的消息。
下面是一个惯用法的简单示例
# myapp.py
import logging
import mylib
logger = logging.getLogger(__name__)
def main():
logging.basicConfig(filename='myapp.log', level=logging.INFO)
logger.info('Started')
mylib.do_something()
logger.info('Finished')
if __name__ == '__main__':
main()
# mylib.py
import logging
logger = logging.getLogger(__name__)
def do_something():
logger.info('Doing something')
如果运行 myapp.py,你应该在 myapp.log 中看到如下内容
INFO:__main__:Started
INFO:mylib:Doing something
INFO:__main__:Finished
这种惯用法的关键特点是,大多数代码只是简单地使用 getLogger(__name__) 创建一个模块级别的 logger,并使用该 logger 进行任何需要的日志记录。这种方式简洁,同时允许下游代码在需要时进行细粒度控制。记录到模块级 logger 的消息会被转发到更高级别模块中 logger 的处理器,一直上传到最高级的 logger,即根 logger;这种方法被称为分层日志记录。
为了使日志记录有用,需要对其进行配置:为每个 logger 设置级别和目标,可能还需要更改特定模块的日志记录方式,通常基于命令行参数或应用程序配置。在大多数情况下,像上面那样,只需要配置根 logger,因为所有较低级别的模块级 logger 最终都会将它们的消息转发到其处理器。 basicConfig() 提供了一种快速配置根 logger 的方法,可以处理许多用例。
该模块提供了许多功能和灵活性。如果你不熟悉日志记录,掌握它的最好方法是查看教程(请参阅上方和右侧的链接)。
该模块定义的基本类及其属性和方法,将在下面各节中列出。
Logger 暴露了应用程序代码直接使用的接口。
Handler 将日志记录(由 logger 创建)发送到适当的目标。
Filter 提供了更细粒度的功能,用于确定要输出哪些日志记录。
Formatter 指定了最终输出中日志记录的布局。
Logger 对象¶
Logger 具有以下属性和方法。请注意,Logger 永远不应直接实例化,而应始终通过模块级函数 logging.getLogger(name)。多次使用相同的名称调用 getLogger() 将始终返回对同一个 Logger 对象的引用。
name 是一个潜在的以点分隔的层级值,例如 foo.bar.baz(尽管它也可以是简单的 foo)。在层级列表中位置更靠下的 Logger 是列表中位置更靠上的 Logger 的子级。例如,给定一个名为 foo 的 Logger,名为 foo.bar、foo.bar.baz 和 foo.bam 的 Logger 都是 foo 的后代。此外,所有 Logger 都是根 Logger 的后代。Logger 名称层级结构类似于 Python 包层级结构,如果你使用推荐的构造 logging.getLogger(__name__) 按模块组织 Logger,则两者是相同的。这是因为在一个模块中,__name__ 是该模块在 Python 包命名空间中的名称。
- class logging.Logger¶
- name¶
这是 logger 的名称,是传递给
getLogger()以获取该 logger 的值。备注
此属性应被视为只读。
- level¶
此 logger 的阈值,由
setLevel()方法设置。备注
不要直接设置此属性——应始终使用
setLevel(),它会对传递的级别进行检查。
- parent¶
此 logger 的父 logger。它可能会根据稍后实例化的、在命名空间层级中位置更高的 logger 而改变。
备注
此值应被视为只读。
- propagate¶
如果此属性的计算结果为真,则记录到此 logger 的事件将传递给更高级别(祖先)logger 的处理器,此外还会传递给附加到此 logger 的任何处理器。消息会直接传递给祖先 logger 的处理器——不会考虑相关祖先 logger 的级别或过滤器。
如果此属性的计算结果为假,则日志消息不会传递给祖先 logger 的处理器。
举个例子详细说明:如果名为
A.B.C的 logger 的 propagate 属性的计算结果为真,任何通过logging.getLogger('A.B.C').error(...)等方法调用记录到A.B.C的事件,[在通过该 logger 的级别和过滤器设置后] 将依次传递给附加到名为A.B、A和根 logger 的任何处理器,在此之前会先传递给附加到A.B.C的任何处理器。如果A.B.C、A.B、A链中的任何 logger 的propagate属性设置为假,那么该 logger 就是最后一个其处理器有机会处理该事件的 logger,传播在该点停止。构造函数将此属性设置为
True。备注
如果你将一个处理器附加到一个 logger *和* 它的一个或多个祖先上,它可能会多次发出相同的记录。通常情况下,你不需要将一个处理器附加到多个 logger 上——如果你只将它附加到 logger 层级中最高的适当 logger,那么它将看到所有后代 logger 记录的所有事件,前提是它们的 propagate 设置保持为
True。一个常见的场景是仅将处理器附加到根 logger,让传播来处理其余部分。
- handlers¶
直接附加到此 logger 实例的处理器列表。
备注
此属性应被视为只读;它通常通过
addHandler()和removeHandler()方法更改,这些方法使用锁来确保线程安全操作。
- disabled¶
此属性禁用对任何事件的处理。它在初始化器中被设置为
False,并且只由日志记录配置代码更改。备注
此属性应被视为只读。
- setLevel(level)¶
将此 logger 的阈值设置为 level。严重性低于 level 的日志消息将被忽略;严重性为 level 或更高的日志消息将由此 logger 的一个或多个处理器发出,除非某个处理器的级别已设置为比 level 更高的严重性级别。
当一个 logger 被创建时,级别被设置为
NOTSET(当该 logger 是根 logger 时,这将导致所有消息被处理;当该 logger 是非根 logger 时,这将导致委托给父级)。请注意,根 logger 创建时的级别为WARNING。术语“委托给父级”意味着,如果一个 logger 的级别为 NOTSET,则会遍历其祖先 logger 链,直到找到一个级别不是 NOTSET 的祖先,或者到达根 logger。
如果找到了一个级别不是 NOTSET 的祖先,则该祖先的级别被视为开始祖先搜索的 logger 的有效级别,并用于确定如何处理日志事件。
如果到达了根 logger,并且其级别为 NOTSET,则所有消息都将被处理。否则,根 logger 的级别将被用作有效级别。
有关级别列表,请参阅日志级别。
在 3.2 版本发生变更: level 参数现在接受级别的字符串表示形式,例如 'INFO',作为整数常量(如
INFO)的替代。但请注意,级别在内部存储为整数,getEffectiveLevel()和isEnabledFor()等方法将返回/期望被传递整数。
- isEnabledFor(level)¶
指示此 logger 是否会处理严重性为 level 的消息。此方法首先检查由
logging.disable(level)设置的模块级级别,然后检查由getEffectiveLevel()确定的 logger 的有效级别。
- getEffectiveLevel()¶
指示此 logger 的有效级别。如果已使用
setLevel()设置了非NOTSET的值,则返回该值。否则,将向根方向遍历层级结构,直到找到一个非NOTSET的值,并返回该值。返回的值是一个整数,通常是logging.DEBUG、logging.INFO等之一。
- getChild(suffix)¶
返回一个作为此 logger 后代的 logger,由后缀确定。因此,
logging.getLogger('abc').getChild('def.ghi')将返回与logging.getLogger('abc.def.ghi')相同的 logger。这是一个方便的方法,当父 logger 使用例如__name__而不是字面量字符串命名时非常有用。在 3.2 版本加入。
- getChildren()¶
返回一组作为此 logger 直接子级的 logger。例如,
logging.getLogger().getChildren()可能会返回一个包含名为foo和bar的 logger 的集合,但名为foo.bar的 logger 不会包含在该集合中。同样,logging.getLogger('foo').getChildren()可能会返回一个包含名为foo.bar的 logger 的集合,但不会包含名为foo.bar.baz的 logger。3.12 新版功能.
- debug(msg, *args, **kwargs)¶
在此 logger 上记录一条级别为
DEBUG的消息。msg 是消息格式字符串,args 是使用字符串格式化操作符合并到 msg 中的参数。(请注意,这意味着你可以在格式字符串中使用关键字,并附带一个字典参数。)如果没有提供 args,则不会对 msg 执行 % 格式化操作。在 kwargs 中有四个被检查的关键字参数:exc_info、stack_info、stacklevel 和 extra。
如果 exc_info 的计算结果不为 false,则会将异常信息添加到日志消息中。如果提供了异常元组(格式与
sys.exc_info()返回的相同)或异常实例,则使用它;否则,将调用sys.exc_info()来获取异常信息。第二个可选的关键字参数是 stack_info,默认为
False。如果为真,则会将堆栈信息(包括实际的日志调用)添加到日志消息中。请注意,这与通过指定 exc_info 显示的堆栈信息不同:前者是从堆栈底部到当前线程中日志调用的堆栈帧,而后者是关于在搜索异常处理器时,因异常而展开的堆栈帧信息。你可以独立于 exc_info 指定 stack_info,例如,即使没有引发异常,也可以显示你是如何到达代码中某个特定点的。堆栈帧会在一个标题行后打印,该标题行显示:
Stack (most recent call last):
这模仿了显示异常帧时使用的
Traceback (most recent call last):。第三个可选的关键字参数是 stacklevel,默认为
1。如果大于 1,在计算为日志事件创建的LogRecord中设置的行号和函数名时,将跳过相应数量的堆栈帧。这可以在日志记录辅助函数中使用,以便记录的函数名、文件名和行号不是辅助函数/方法的信息,而是其调用者的信息。此参数的名称与warnings模块中的等效参数相呼应。第四个关键字参数是 extra,可用于传递一个字典,该字典用于使用用户定义的属性填充为日志事件创建的
LogRecord的__dict__。然后,你可以随心所欲地使用这些自定义属性。例如,可以将它们合并到日志消息中。例如:FORMAT = '%(asctime)s %(clientip)-15s %(user)-8s %(message)s' logging.basicConfig(format=FORMAT) d = {'clientip': '192.168.0.1', 'user': 'fbloggs'} logger = logging.getLogger('tcpserver') logger.warning('Protocol problem: %s', 'connection reset', extra=d)
会打印类似如下的内容:
2006-02-08 22:20:02,165 192.168.0.1 fbloggs Protocol problem: connection reset
在 extra 中传递的字典中的键不应与日志系统使用的键冲突。(有关日志系统使用哪些键的更多信息,请参阅 LogRecord 属性 部分。)
如果你选择在日志消息中使用这些属性,你需要小心一些。例如,在上面的例子中,
Formatter的格式字符串期望LogRecord的属性字典中有 'clientip' 和 'user'。如果缺少这些,消息将不会被记录,因为会发生字符串格式化异常。因此,在这种情况下,你总是需要传递带有这些键的 extra 字典。虽然这可能很烦人,但此功能旨在用于特殊情况,例如多线程服务器,其中相同的代码在许多上下文中执行,并且出现的有趣条件取决于此上下文(如上面示例中的远程客户端 IP 地址和已认证的用户名)。在这种情况下,很可能会使用专门的
Formatter和特定的Handler。如果没有处理器附加到此 logger(或其任何祖先,考虑到相关的
Logger.propagate属性),消息将被发送到lastResort上设置的处理器。在 3.2 版本发生变更: 添加了 stack_info 参数。
在 3.5 版本发生变更: exc_info 参数现在可以接受异常实例。
在 3.8 版本发生变更: 添加了 stacklevel 参数。
- warning(msg, *args, **kwargs)¶
在此 logger 上记录一条级别为
WARNING的消息。参数的解释与debug()相同。备注
有一个已过时的方法
warn,其功能与warning完全相同。由于warn已被弃用,请不要使用它——请改用warning。
- exception(msg, *args, **kwargs)¶
在此 logger 上记录一条级别为
ERROR的消息。参数的解释与debug()相同。异常信息会添加到日志消息中。此方法只应在异常处理器中调用。
- addFilter(filter)¶
将指定的过滤器 filter 添加到此 logger。
- removeFilter(filter)¶
从此 logger 中移除指定的过滤器 filter。
- filter(record)¶
将此 logger 的过滤器应用于记录,如果记录需要处理,则返回
True。过滤器会依次被查询,直到其中一个返回 false 值。如果没有一个返回 false 值,记录将被处理(传递给处理器)。如果有一个返回 false 值,则不会对该记录进行进一步处理。
- addHandler(hdlr)¶
将指定的处理器 hdlr 添加到此 logger。
- removeHandler(hdlr)¶
从此 logger 中移除指定的处理器 hdlr。
- findCaller(stack_info=False, stacklevel=1)¶
查找调用者的源文件名和行号。以一个 4 元素元组的形式返回文件名、行号、函数名和堆栈信息。除非 stack_info 为
True,否则堆栈信息将作为None返回。stacklevel 参数从调用
debug()和其他 API 的代码中传递。如果大于 1,多出的部分将用于在确定要返回的值之前跳过堆栈帧。这在从辅助/包装代码调用日志记录 API 时通常很有用,以便事件日志中的信息不是指向辅助/包装代码,而是指向调用它的代码。
- handle(record)¶
通过将记录传递给与此 logger 及其祖先相关联的所有处理器(直到找到一个 propagate 值为 false 的 logger)来处理该记录。此方法用于处理从套接字接收到的反序列化记录,以及本地创建的记录。使用
filter()应用 logger 级别的过滤。
- makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)¶
这是一个工厂方法,可以在子类中重写以创建专门的
LogRecord实例。
- hasHandlers()¶
检查此 logger 是否配置了任何处理器。这是通过在此 logger 及其在 logger 层级中的父级中查找处理器来完成的。如果找到处理器,则返回
True,否则返回False。当找到一个“propagate”属性设置为 false 的 logger 时,该方法将停止向上搜索层级结构——这将是最后一个被检查是否存在处理器的 logger。在 3.2 版本加入。
在 3.7 版本发生变更: Logger 现在可以被序列化和反序列化。
日志级别¶
日志级别的数值在下表中给出。如果你想定义自己的级别,并且需要它们相对于预定义级别具有特定的值,这些数值就特别重要。如果你定义了一个具有相同数值的级别,它将覆盖预定义的值;预定义的名称将丢失。
级别 |
数值 |
含义/何时使用 |
|---|---|---|
|
0 |
当在 logger 上设置时,表示将咨询祖先 logger 以确定有效级别。如果最终仍解析为 |
|
10 |
详细信息,通常只对试图诊断问题的开发人员感兴趣。 |
|
20 |
确认事情按预期工作。 |
|
30 |
表示发生了一些意外情况,或者在不久的将来可能会出现问题(例如“磁盘空间不足”)。软件仍在按预期工作。 |
|
40 |
由于更严重的问题,软件无法执行某些功能。 |
|
50 |
严重错误,表示程序本身可能无法继续运行。 |
Handler 对象¶
Handler 具有以下属性和方法。请注意,Handler 永远不会被直接实例化;此类作为更有用的子类的基类。但是,子类中的 __init__() 方法需要调用 Handler.__init__()。
- class logging.Handler¶
- __init__(level=NOTSET)¶
通过设置其级别、将过滤器列表设置为空列表以及创建一个锁(使用
createLock())来初始化Handler实例,以序列化对 I/O 机制的访问。
- createLock()¶
初始化一个线程锁,可用于序列化对可能不是线程安全的底层 I/O 功能的访问。
- acquire()¶
获取使用
createLock()创建的线程锁。
- setLevel(level)¶
将此处理器的阈值设置为 level。严重性低于 level 的日志消息将被忽略。当创建处理器时,级别被设置为
NOTSET(这将导致所有消息被处理)。有关级别列表,请参阅日志级别。
在 3.2 版本发生变更: level 参数现在接受级别的字符串表示形式,例如 'INFO',作为整数常量(如
INFO)的替代。
- addFilter(filter)¶
将指定的过滤器 filter 添加到此处理器。
- removeFilter(filter)¶
从此处理器中移除指定的过滤器 filter。
- filter(record)¶
将此处理器的过滤器应用于记录,如果记录需要处理,则返回
True。过滤器会依次被查询,直到其中一个返回 false 值。如果没有一个返回 false 值,记录将被发出。如果有一个返回 false 值,处理器将不会发出该记录。
- flush()¶
确保所有日志输出都已刷新。此版本不做任何事情,旨在由子类实现。
- handle(record)¶
根据可能已添加到处理器的过滤器,有条件地发出指定的日志记录。用 I/O 线程锁的获取/释放来包装记录的实际发出过程。
- handleError(record)¶
当在
emit()调用期间遇到异常时,处理器应调用此方法。如果模块级属性raiseExceptions为False,异常将被静默忽略。这对于日志记录系统来说是大多数情况下所期望的——大多数用户不关心日志记录系统中的错误,他们更关心应用程序错误。但是,如果你愿意,可以用自定义处理器替换它。指定的记录是发生异常时正在处理的记录。(raiseExceptions的默认值为True,因为这在开发过程中更有用)。
- format(record)¶
对记录进行格式化——如果设置了格式化器,则使用它。否则,使用模块的默认格式化器。
- emit(record)¶
执行任何必要的操作以实际记录指定的日志记录。此版本旨在由子类实现,因此会引发
NotImplementedError。警告
此方法在获取处理器级锁之后被调用,锁在此方法返回后被释放。当你重写此方法时,请注意,在调用任何可能调用日志 API 其他部分(可能会进行锁定)的代码时应小心,因为这可能导致死锁。具体来说:
日志记录配置 API 会获取模块级锁,然后在配置这些处理器时获取各个处理器级锁。
许多日志记录 API 会锁定模块级锁。如果从此方法中调用此类 API,可能会导致死锁,如果在另一个线程上进行了配置调用,因为该线程会尝试在处理器级锁*之前*获取模块级锁,而此线程则尝试在处理器级锁*之后*获取模块级锁(因为在此方法中,处理器级锁已经被获取)。
有关作为标准包含的处理器列表,请参阅 logging.handlers。
Formatter 对象¶
- class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)¶
负责将
LogRecord转换为供人或外部系统解释的输出字符串。- 参数:
fmt (str) – 用于整个日志输出的给定 style 的格式字符串。可能的映射键取自
LogRecord对象的 LogRecord 属性。如果未指定,则使用'%(message)s',这只是记录的消息。datefmt (str) – 用于日志输出日期/时间部分的格式字符串。如果未指定,则使用
formatTime()中描述的默认格式。style (str) – 可以是
'%'、'{'或'$'之一,决定了格式字符串将如何与其数据合并:使用 printf 风格的字符串格式化 (%)、str.format()({) 或string.Template($)。这仅适用于 fmt(例如'%(message)s'对比'{message}'),不适用于传递给日志记录方法的实际日志消息。但是,有其他方法可以对日志消息使用{和$格式化。validate (bool) – 如果为
True(默认值),不正确或不匹配的 fmt 和 style 将引发ValueError;例如,logging.Formatter('%(asctime)s - %(message)s', style='{')。defaults (dict[str, Any]) – 一个包含在自定义字段中使用的默认值的字典。例如,
logging.Formatter('%(ip)s %(message)s', defaults={"ip": None})
在 3.2 版本发生变更: 添加了 style 参数。
在 3.8 版本发生变更: 添加了 validate 参数。
在 3.10 版本发生变更: 添加了 defaults 参数。
- format(record)¶
记录的属性字典用作字符串格式化操作的操作数。返回结果字符串。在格式化字典之前,会执行一些准备步骤。记录的 message 属性是使用 msg % args 计算的。如果格式化字符串包含
'(asctime)',则调用formatTime()来格式化事件时间。如果有异常信息,则使用formatException()格式化并附加到消息中。请注意,格式化的异常信息缓存在属性 exc_text 中。这很有用,因为异常信息可以被序列化并通过网络发送,但如果你有多个自定义异常信息格式化的Formatter子类,则应小心。在这种情况下,在一个格式化器完成其格式化后,你将需要清除缓存的值(通过将 exc_text 属性设置为None),以便下一个处理该事件的格式化器不会使用缓存的值,而是重新计算它。如果堆栈信息可用,它会附加在异常信息之后,使用
formatStack()在必要时进行转换。
- formatTime(record, datefmt=None)¶
想要使用格式化时间的格式化器应从
format()中调用此方法。此方法可以在格式化器中重写以提供任何特定要求,但基本行为如下:如果指定了 datefmt(一个字符串),则使用time.strftime()来格式化记录的创建时间。否则,使用格式 '%Y-%m-%d %H:%M:%S,uuu',其中 uuu 部分是毫秒值,其他字母与time.strftime()文档中的相同。此格式的时间示例为2003-01-23 00:29:50,411。返回结果字符串。此函数使用用户可配置的函数将创建时间转换为元组。默认情况下,使用
time.localtime();要为特定格式化器实例更改此设置,请将converter属性设置为与time.localtime()或time.gmtime()签名相同的函数。要为所有格式化器更改它,例如,如果你希望所有日志记录时间都以 GMT 显示,请在Formatter类中设置converter属性。在 3.3 版本发生变更: 以前,默认格式是硬编码的,如本例所示:
2010-09-06 22:38:15,292,其中逗号前的部分由 strptime 格式字符串('%Y-%m-%d %H:%M:%S')处理,逗号后的部分是毫秒值。由于 strptime 没有毫秒的格式占位符,因此使用另一个格式字符串'%s,%03d'附加毫秒值——这两个格式字符串都已硬编码到此方法中。经过更改后,这些字符串被定义为类级属性,可以在实例级别根据需要重写。这些属性的名称是default_time_format(用于 strptime 格式字符串)和default_msec_format(用于附加毫秒值)。在 3.9 版本发生变更:
default_msec_format可以为None。
- formatException(exc_info)¶
将指定的异常信息(由
sys.exc_info()返回的标准异常元组)格式化为字符串。此默认实现仅使用traceback.print_exception()。返回结果字符串。
- formatStack(stack_info)¶
将指定的堆栈信息(由
traceback.print_stack()返回的字符串,但移除了最后一个换行符)格式化为字符串。此默认实现仅返回输入值。
- class logging.BufferingFormatter(linefmt=None)¶
一个基础格式化器类,适合在要格式化多个记录时进行子类化。你可以传递一个
Formatter实例,用于格式化每一行(对应单个记录)。如果未指定,将使用默认格式化器(仅输出事件消息)作为行格式化器。- formatHeader(records)¶
为 records 列表返回一个页眉。基本实现仅返回空字符串。如果你想要特定的行为,例如显示记录计数、标题或分隔线,则需要重写此方法。
为 records 列表返回一个页脚。基本实现仅返回空字符串。如果你想要特定的行为,例如显示记录计数或分隔线,则需要重写此方法。
- format(records)¶
为 records 列表返回格式化的文本。如果没有记录,基本实现仅返回空字符串;否则,它返回页眉、每个用行格式化器格式化的记录以及页脚的串联。
Filter 对象¶
Filters 可以被 Handlers 和 Loggers 用于比级别提供更复杂的过滤。基础过滤器类只允许 logger 层级中某个点以下的事件。例如,用 'A.B' 初始化的过滤器将允许由 logger 'A.B'、'A.B.C'、'A.B.C.D'、'A.B.D' 等记录的事件,但不允许 'A.BB'、'B.A.B' 等。如果用空字符串初始化,则所有事件都通过。
- class logging.Filter(name='')¶
返回一个
Filter类的实例。如果指定了 name,它命名了一个 logger,该 logger 及其子级的事件将通过过滤器。如果 name 是空字符串,则允许每个事件。- filter(record)¶
指定的记录是否应被记录?返回 false 表示否,true 表示是。过滤器可以就地修改日志记录,也可以返回一个完全不同的记录实例,该实例将在事件的任何未来处理中替换原始日志记录。
请注意,附加到处理器的过滤器在处理器发出事件之前被查询,而附加到 logger 的过滤器在事件被记录时(使用 debug()、info() 等)被查询,在将事件发送到处理器之前。这意味着由后代 logger 生成的事件不会被 logger 的过滤器设置过滤,除非该过滤器也已应用于那些后代 logger。
你实际上不需要子类化 Filter:你可以传递任何具有具有相同语义的 filter 方法的实例。
在 3.2 版本发生变更: 你不需要创建专门的 Filter 类,或使用其他具有 filter 方法的类:你可以使用一个函数(或其他可调用对象)作为过滤器。过滤逻辑将检查过滤器对象是否具有 filter 属性:如果有,则假定它是一个 Filter,并调用其 filter() 方法。否则,假定它是一个可调用对象,并以记录作为单个参数调用它。返回的值应符合 filter() 返回的值。
在 3.12 版本发生变更: 你现在可以从过滤器返回一个 LogRecord 实例来替换日志记录,而不是就地修改它。这允许附加到 Handler 的过滤器在日志记录被发出之前修改它,而不会对其他处理器产生副作用。
尽管过滤器主要用于根据比级别更复杂的标准过滤记录,但它们可以看到附加到的处理器或 logger 处理的每一条记录:这在你想要做一些事情时很有用,比如计算特定 logger 或处理器处理了多少条记录,或者在正在处理的 LogRecord 中添加、更改或移除属性。显然,更改 LogRecord 需要小心,但它确实允许将上下文信息注入到日志中(请参阅使用过滤器传递上下文信息)。
LogRecord 对象¶
LogRecord 实例由 Logger 在每次记录内容时自动创建,也可以通过 makeLogRecord() 手动创建(例如,从通过网络接收到的序列化事件)。
- class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)¶
包含与被记录事件相关的所有信息。
主要信息在 msg 和 args 中传递,它们使用
msg % args组合以创建记录的message属性。- 参数:
name (str) – 用于记录此
LogRecord表示的事件的 logger 名称。请注意,即使它可能由附加到不同(祖先)logger 的处理器发出,LogRecord中的 logger 名称将始终是此值。level (int) – 日志事件的数字级别(例如
DEBUG为10,INFO为20等)。请注意,这会转换为 LogRecord 的*两个*属性:levelno用于数值,levelname用于相应的级别名称。pathname (str) – 进行日志记录调用的源文件的完整字符串路径。
lineno (int) – 进行日志记录调用的源文件中的行号。
msg (Any) – 事件描述消息,可以是一个带有可变数据占位符的 % 格式字符串,或任意对象(请参阅使用任意对象作为消息)。
exc_info (tuple[type[BaseException], BaseException, types.TracebackType] | None) – 包含当前异常信息的异常元组,如
sys.exc_info()返回的,如果没有异常信息可用,则为None。func (str | None) – 调用日志记录调用的函数或方法的名称。
sinfo (str | None) – 一个文本字符串,表示当前线程中从堆栈底部到日志记录调用的堆栈信息。
- getMessage()¶
在合并所有用户提供的参数和消息后,返回此
LogRecord实例的消息。如果提供给日志记录调用的消息参数不是字符串,则会调用str()将其转换为字符串。这允许使用用户定义的类作为消息,这些类的__str__方法可以返回实际要使用的格式字符串。
在 3.2 版本发生变更: 通过提供一个用于创建记录的工厂,使得
LogRecord的创建变得更具可配置性。该工厂可以使用getLogRecordFactory()和setLogRecordFactory()来设置(关于工厂的签名,请参见此)。此功能可用于在创建
LogRecord时注入您自己的值。您可以使用以下模式old_factory = logging.getLogRecordFactory() def record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.custom_attribute = 0xdecafbad return record logging.setLogRecordFactory(record_factory)
通过这种模式,可以链接多个工厂,只要它们不覆盖彼此的属性或无意中覆盖上面列出的标准属性,就不会出现意外情况。
LogRecord 属性¶
LogRecord 有许多属性,其中大部分派生自构造函数的参数。(请注意,LogRecord 构造函数参数和 LogRecord 属性之间的名称并不总是一一对应。)这些属性可用于将记录中的数据合并到格式字符串中。下表按字母顺序列出了属性名称、它们的含义以及在 %-风格格式字符串中对应的占位符。
如果您使用 {}-格式化 (str.format()),您可以在格式字符串中使用 {attrname} 作为占位符。如果您使用 $-格式化 (string.Template),请使用 ${attrname} 的形式。在这两种情况下,当然,请将 attrname 替换为您想要使用的实际属性名称。
在使用 {}-格式化时,您可以通过在属性名后放置格式化标志来指定它们,用冒号分隔。例如:占位符 {msecs:03.0f} 会将毫秒值 4 格式化为 004。有关可用选项的完整详细信息,请参阅 str.format() 文档。
属性名称 |
格式 |
描述 |
|---|---|---|
args |
您应该不需要自己格式化这个。 |
合并到 |
asctime |
|
创建 |
created |
|
创建 |
exc_info |
您应该不需要自己格式化这个。 |
异常元组(类似于 |
filename |
|
|
funcName |
|
包含日志记录调用的函数名称。 |
levelname |
|
消息的文本日志记录级别( |
levelno |
|
|
lineno |
|
发出日志记录调用的源代码行号(如果可用)。 |
message |
|
记录的消息,计算为 |
模块 |
|
模块( |
msecs |
|
创建 |
msg |
您应该不需要自己格式化这个。 |
在原始日志记录调用中传递的格式字符串。与 |
名称 |
|
用于记录调用的日志记录器的名称。 |
pathname |
|
发出日志记录调用的源文件的完整路径名(如果可用)。 |
process (进程) |
|
进程ID(如果可用)。 |
processName |
|
进程名称(如果可用)。 |
relativeCreated |
|
创建 LogRecord 的时间(以毫秒为单位),相对于加载日志模块的时间。 |
stack_info |
您应该不需要自己格式化这个。 |
堆栈帧信息(如果可用),从当前线程堆栈的底部,一直到导致创建此记录的日志记录调用的堆栈帧。 |
thread |
|
线程ID(如果可用)。 |
threadName |
|
线程名称(如果可用)。 |
taskName |
|
|
3.1 版后已移除: 添加了 processName。
3.12 版后已移除: 添加了 taskName。
LoggerAdapter 对象¶
LoggerAdapter 实例用于方便地将上下文信息传递到日志记录调用中。有关用法示例,请参阅关于 向日志输出添加上下文信息 的部分。
- class logging.LoggerAdapter(logger, extra, merge_extra=False)¶
返回一个
LoggerAdapter实例,该实例使用一个底层的Logger实例、一个类字典对象(extra)和一个布尔值(merge_extra)进行初始化,该布尔值指示是否应将各个日志调用的 extra 参数与LoggerAdapter的 extra 合并。默认行为是忽略各个日志调用的 extra 参数,而只使用LoggerAdapter实例的 extra。- process(msg, kwargs)¶
修改传递给日志记录调用的消息和/或关键字参数,以插入上下文信息。此实现将作为 extra 传递给构造函数的对象,并使用键 ‘extra’ 将其添加到 kwargs 中。返回值是一个 (msg, kwargs) 元组,其中包含传入参数的(可能已修改的)版本。
- manager¶
委托给底层 logger 上的
manager。
- _log¶
委托给底层 logger 上的
_log()方法。
除上述方法外,
LoggerAdapter还支持Logger的以下方法:debug()、info()、warning()、error()、exception()、critical()、log()、isEnabledFor()、getEffectiveLevel()、setLevel()和hasHandlers()。这些方法与Logger中的对应方法具有相同的签名,因此您可以互换使用这两种类型的实例。在 3.2 版本发生变更:
LoggerAdapter中添加了isEnabledFor()、getEffectiveLevel()、setLevel()和hasHandlers()方法。这些方法会委托给底层的日志记录器。在 3.6 版本发生变更: 添加了属性
manager和方法_log(),它们委托给底层的日志记录器,并允许适配器嵌套。在 3.13 版本发生变更: 增加了 merge_extra 参数。
线程安全¶
日志模块旨在无需客户端进行任何特殊工作即可实现线程安全。它通过使用线程锁来实现这一点;有一个锁用于序列化对模块共享数据的访问,每个处理器也创建一个锁来序列化对其底层 I/O 的访问。
如果您使用 signal 模块实现异步信号处理程序,您可能无法在此类处理程序中使用日志记录。这是因为 threading 模块中的锁实现并不总是可重入的,因此不能从此类信号处理程序中调用。
模块级函数¶
除了上面描述的类之外,还有许多模块级别的函数。
- logging.getLogger(name=None)¶
返回具有指定名称的日志记录器,如果名称为
None,则返回层次结构中的根日志记录器。如果指定了名称,则通常是点分隔的层级名称,如 ‘a’、‘a.b’ 或 ‘a.b.c.d’。这些名称的选择完全取决于使用日志记录的开发人员,但建议使用__name__,除非您有特殊原因不这样做,如 日志记录器对象 中所述。所有使用给定名称对此函数的调用都返回相同的日志记录器实例。这意味着日志记录器实例永远不需要在应用程序的不同部分之间传递。
- logging.getLoggerClass()¶
返回标准的
Logger类,或传递给setLoggerClass()的最后一个类。此函数可以在新的类定义中调用,以确保安装自定义的Logger类不会撤销其他代码已经应用的自定义设置。例如class MyLogger(logging.getLoggerClass()): # ... override behaviour here
- logging.getLogRecordFactory()¶
返回一个用于创建
LogRecord的可调用对象。在 3.2 版本加入: 提供了此函数以及
setLogRecordFactory(),以允许开发人员更好地控制表示日志事件的LogRecord的构造方式。有关如何调用工厂的更多信息,请参见
setLogRecordFactory()。
- logging.debug(msg, *args, **kwargs)¶
这是一个方便的函数,它在根日志记录器上调用
Logger.debug()。参数的处理方式与该方法中描述的完全相同。唯一的区别是,如果根日志记录器没有处理程序,则在根日志记录器上调用
debug之前会调用basicConfig()。对于非常短的脚本或
logging功能的快速演示,debug和其他模块级函数可能很方便。但是,大多数程序会希望仔细并明确地控制日志配置,因此应优先创建模块级日志记录器,并在其上调用Logger.debug()(或其他特定级别的方法),如本文档开头所述。
- logging.warning(msg, *args, **kwargs)¶
在根日志记录器上记录一条级别为
WARNING的消息。参数和行为与debug()相同。备注
有一个已过时的函数
warn,其功能与warning相同。由于warn已被弃用,请不要使用它 - 请改用warning。
- logging.exception(msg, *args, **kwargs)¶
在根日志记录器上记录一条级别为
ERROR的消息。参数和行为与debug()相同。异常信息会添加到日志消息中。此函数只应在异常处理程序中调用。
- logging.disable(level=CRITICAL)¶
为所有日志记录器提供一个覆盖级别 level,该级别优先于日志记录器自身的级别。当需要临时限制整个应用程序的日志输出时,此函数非常有用。它的效果是禁用所有严重性为 level 及以下的日志调用,因此如果您使用 INFO 值调用它,则所有 INFO 和 DEBUG 事件都将被丢弃,而严重性为 WARNING 及以上的事件将根据日志记录器的有效级别进行处理。如果调用
logging.disable(logging.NOTSET),它会有效地移除此覆盖级别,以便日志输出再次取决于各个日志记录器的有效级别。请注意,如果您定义了任何高于
CRITICAL的自定义日志级别(不推荐这样做),您将无法依赖 level 参数的默认值,而必须显式提供一个合适的值。在 3.7 版本发生变更: level 参数默认设置为
CRITICAL级别。有关此更改的更多信息,请参阅 bpo-28524。
- logging.addLevelName(level, levelName)¶
在一个内部字典中将级别 level 与文本 levelName 关联,该字典用于将数字级别映射到文本表示,例如当
Formatter格式化消息时。此函数也可用于定义您自己的级别。唯一的限制是,所有使用的级别都必须使用此函数注册,级别应该是正整数,并且它们应该按严重性递增的顺序增加。备注
如果您正在考虑定义自己的级别,请参阅关于 自定义级别 的部分。
- logging.getLevelNamesMapping()¶
返回从级别名称到其相应日志级别的映射。例如,字符串 "CRITICAL" 映射到
CRITICAL。每次调用此函数时,返回的映射都是从内部映射中复制的。在 3.11 版本中新增。
- logging.getLevelName(level)¶
返回日志级别 level 的文本或数字表示。
如果 level 是预定义级别
CRITICAL、ERROR、WARNING、INFO或DEBUG之一,则您会得到相应的字符串。如果您已使用addLevelName()将级别与名称关联,则返回您与 level 关联的名称。如果传入与定义级别之一对应的数值,则返回相应的字符串表示。level 参数也接受级别的字符串表示,例如 'INFO'。在这种情况下,此函数返回该级别对应的数值。
如果没有匹配的数字或字符串值传入,则返回字符串 ‘Level %s’ % level。
备注
级别在内部是整数(因为它们需要在日志逻辑中进行比较)。此函数用于在整数级别和通过
%(levelname)s格式说明符(参见 LogRecord 属性)在格式化日志输出中显示的级别名称之间进行转换,反之亦然。在 3.4 版本发生变更: 在 Python 3.4 之前的版本中,此函数也可以传递一个文本级别,并返回该级别的相应数值。这种未文档化的行为被认为是一个错误,并在 Python 3.4 中被移除,但在 3.4.2 中为了保持向后兼容性而恢复。
- logging.getHandlerByName(name)¶
返回具有指定 name 的处理器,如果不存在该名称的处理器,则返回
None。3.12 新版功能.
- logging.getHandlerNames()¶
返回一个包含所有已知处理器名称的不可变集合。
3.12 新版功能.
- logging.makeLogRecord(attrdict)¶
创建并返回一个新的
LogRecord实例,其属性由 attrdict 定义。此函数对于获取一个通过套接字发送的、经过 pickle 处理的LogRecord属性字典,并在接收端将其重构为LogRecord实例非常有用。
- logging.basicConfig(**kwargs)¶
通过创建一个带有默认
Formatter的StreamHandler并将其添加到根日志记录器,为日志系统进行基本配置。函数debug()、info()、warning()、error()和critical()会在根日志记录器没有定义处理器时自动调用basicConfig()。如果根日志记录器已经配置了处理器,此函数将不执行任何操作,除非关键字参数 force 设置为
True。备注
在启动其他线程之前,应从主线程调用此函数。在 Python 2.7.1 和 3.2 之前的版本中,如果从多个线程调用此函数,可能会(在极少数情况下)将处理器多次添加到根日志记录器,导致意外结果,例如日志中消息重复。
支持以下关键字参数。
格式
描述
filename
指定创建一个
FileHandler,使用指定的文件名,而不是一个StreamHandler。filemode
如果指定了 filename,则以此 模式 打开文件。默认为
'a'。format
为处理器使用指定的格式字符串。默认为由冒号分隔的属性
levelname、name和message。datefmt
使用指定的日期/时间格式,该格式被
time.strftime()接受。风格
如果指定了 format,则为格式字符串使用此样式。可以是
'%'、'{'或'$'之一,分别对应 printf 风格、str.format()或string.Template。默认为'%'。level
将根日志记录器级别设置为指定的 级别。
stream
使用指定的流来初始化
StreamHandler。请注意,此参数与 filename 不兼容——如果两者都存在,则会引发ValueError。handlers
如果指定,这应该是一个包含已创建处理器的可迭代对象,用于添加到根日志记录器。任何尚未设置格式化器的处理器都将被分配此函数中创建的默认格式化器。请注意,此参数与 filename 或 stream 不兼容——如果两者都存在,则会引发
ValueError。force
如果此关键字参数指定为 true,则在执行其他参数指定的配置之前,将删除并关闭附加到根日志记录器的任何现有处理器。
编码
如果此关键字参数与 filename 一起指定,其值将在创建
FileHandler时使用,因此在打开输出文件时也会使用。错误
如果此关键字参数与 filename 一起指定,其值将在创建
FileHandler时使用,因此在打开输出文件时也会使用。如果未指定,则使用值 ‘backslashreplace’。请注意,如果指定了None,它将如此传递给open(),这意味着它将被视为与传递 ‘errors’ 相同。在 3.2 版本发生变更: 添加了 style 参数。
在 3.3 版本发生变更: 添加了 handlers 参数。添加了额外的检查来捕获指定不兼容参数的情况(例如,handlers 与 stream 或 filename 一起,或者 stream 与 filename 一起)。
在 3.8 版本发生变更: 添加了 force 参数。
在 3.9 版本发生变更: 添加了 encoding 和 errors 参数。
- logging.shutdown()¶
通知日志系统通过刷新和关闭所有处理器来执行有序关闭。这应该在应用程序退出时调用,并且在此调用之后不应再使用日志系统。
当导入日志模块时,它会将此函数注册为退出处理器(参见
atexit),因此通常不需要手动执行此操作。
- logging.setLoggerClass(klass)¶
告诉日志系统在实例化日志记录器时使用类 klass。该类应定义
__init__(),使其仅需要一个 name 参数,并且__init__()应调用Logger.__init__()。此函数通常在需要使用自定义日志记录器行为的应用程序实例化任何日志记录器之前调用。此调用之后,与任何其他时候一样,不要直接使用子类实例化日志记录器:继续使用logging.getLogger()API 获取您的日志记录器。
- logging.setLogRecordFactory(factory)¶
设置一个用于创建
LogRecord的可调用对象。- 参数:
factory – 用于实例化日志记录的工厂可调用对象。
在 3.2 版本加入: 提供了此函数以及
getLogRecordFactory(),以允许开发人员更好地控制表示日志事件的LogRecord的构造方式。工厂具有以下签名
factory(name, level, fn, lno, msg, args, exc_info, func=None, sinfo=None, **kwargs)- name:
日志记录器名称。
- level:
日志级别(数字)。
- fn:
进行日志记录调用的文件的完整路径名。
- lno:
进行日志记录调用的文件中的行号。
- msg:
日志消息。
- args:
日志消息的参数。
- exc_info:
一个异常元组,或
None。- func:
调用日志记录调用的函数或方法的名称。
- sinfo:
一个堆栈回溯,例如由
traceback.print_stack()提供的,显示调用层次结构。- kwargs:
额外的关键字参数。
模块级属性¶
- logging.lastResort¶
通过此属性可获得一个“最后的处理器”。这是一个
StreamHandler,它写入sys.stderr,级别为WARNING,用于在没有任何日志配置的情况下处理日志事件。最终结果是仅将消息打印到sys.stderr。这取代了早期的错误消息,即“找不到日志记录器 XYZ 的处理器”。如果您出于某种原因需要早期的行为,可以将lastResort设置为None。在 3.2 版本加入。
- logging.raiseExceptions¶
用于查看在处理期间是否应传播异常。
默认值:
True。如果
raiseExceptions为False,则异常会被静默忽略。这对于日志系统来说是大多数情况下所期望的——大多数用户不关心日志系统中的错误,他们更关心应用程序错误。
与 warnings 模块集成¶
captureWarnings() 函数可用于将 logging 与 warnings 模块集成。
- logging.captureWarnings(capture)¶
此函数用于打开和关闭由日志记录捕获警告的功能。
如果 capture 为
True,由warnings模块发出的警告将被重定向到日志系统。具体来说,警告将使用warnings.formatwarning()进行格式化,并将结果字符串记录到一个名为'py.warnings'的日志记录器中,严重性为WARNING。如果 capture 为
False,警告到日志系统的重定向将停止,警告将被重定向到其原始目的地(即在调用captureWarnings(True)之前生效的目的地)。
参见
- 模块
logging.config 日志模块的配置 API。
- 模块
logging.handlers 日志模块附带的有用的处理器。
- PEP 282 - 日志系统
该提案描述了此功能,以便包含在 Python 标准库中。
- 原始 Python 日志包
这是
logging包的原始来源。此站点提供的包版本适用于 Python 1.5.2、2.1.x 和 2.2.x,这些版本在标准库中不包含logging包。