logging - Python 的日志记录工具

源代码: Lib/logging/__init__.py


此模块定义了为应用程序和库实现灵活的事件日志记录系统的函数和类。

由标准库模块提供日志记录 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__) 创建一个模块级记录器,并使用该记录器执行任何需要的日志记录。这很简洁,同时允许下游代码在需要时进行细粒度控制。记录到模块级记录器的消息会被转发到更高级别模块中的记录器处理器,一直到被称为根记录器的最高级别记录器;这种方法被称为分层日志记录。

为了使日志记录有用,需要对其进行配置:设置每个记录器的级别和目标,可能还需要根据命令行参数或应用程序配置更改特定模块的日志记录方式。在大多数情况下,如上面的示例,只需要配置根记录器,因为模块级别所有较低级别的记录器最终都会将其消息转发到其处理器。basicConfig() 提供了一种快速配置根记录器的方法,可以处理许多用例。

该模块提供了许多功能和灵活性。如果您不熟悉日志记录,最好的入门方法是查看教程(请参阅上方和右侧的链接)。

模块定义的基本类及其属性和方法列在以下部分中。

  • 记录器公开了应用程序代码直接使用的接口。

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

  • 过滤器提供了一种更细粒度的工具,用于确定要输出哪些日志记录。

  • 格式化器指定最终输出中日志记录的布局。

记录器对象

记录器具有以下属性和方法。请注意,记录器永远不应该直接实例化,而应该始终通过模块级函数 logging.getLogger(name) 实例化。多次调用具有相同名称的 getLogger() 将始终返回对同一个记录器对象的引用。

name 可能是一个用句点分隔的层次结构值,例如 foo.bar.baz(尽管它也可以是简单的 foo)。层次结构列表中较低的记录器是列表中较高级别记录器的子级。例如,给定一个名称为 foo 的记录器,名称为 foo.barfoo.bar.bazfoo.bam 的记录器都是 foo 的后代。此外,所有记录器都是根记录器的后代。记录器名称层次结构类似于 Python 包层次结构,如果您使用推荐的构造函数 logging.getLogger(__name__) 按模块组织记录器,则它们是相同的。这是因为在模块中,__name__ 是模块在 Python 包命名空间中的名称。

class logging.Logger
name

这是记录器的名称,是传递给 getLogger() 以获取记录器的值。

注意

此属性应视为只读。

level

此记录器的阈值,由 setLevel() 方法设置。

注意

不要直接设置此属性 - 始终使用 setLevel(),它会检查传递给它的级别。

parent

此记录器的父记录器。它可能会根据稍后在命名空间层次结构中更高级别实例化的记录器而改变。

注意

此值应视为只读。

propagate

如果此属性的值为 true,则记录到此记录器的事件将传递到更高级别(祖先)记录器的处理器,以及附加到此记录器的任何处理器。消息直接传递到祖先记录器的处理器 - 不会考虑相关祖先记录器的级别或过滤器。

如果此属性的值为 false,则不会将日志消息传递到祖先记录器的处理器。

用一个例子来说明:如果名为 A.B.C 的记录器的 propagate 属性的值为 true,则通过方法调用(例如 logging.getLogger('A.B.C').error(...))记录到 A.B.C 的任何事件都将[在通过该记录器的级别和过滤器设置后]依次传递到附加到名为 A.BA 和根记录器的任何处理器,然后再传递到附加到 A.B.C 的任何处理器。如果链 A.B.CA.BA 中的任何记录器的 propagate 属性设置为 false,则该记录器是其处理器被提供事件处理的最后一个记录器,并且传播在该点停止。

构造函数将此属性设置为 True

注意

如果您将处理器附加到一个记录器以及它的一个或多个祖先,它可能会多次发出相同的记录。通常,您不需要将处理器附加到多个记录器 - 如果您只将它附加到记录器层次结构中适当的最高级别记录器,则它将看到所有后代记录器记录的所有事件,前提是它们的 propagate 设置保持为 True。一种常见的情况是只将处理器附加到根记录器,并让传播机制处理其余部分。

handlers

直接附加到此记录器实例的处理器列表。

注意

此属性应视为只读;它通常通过 addHandler()removeHandler() 方法更改,这些方法使用锁来确保线程安全操作。

disabled

此属性禁用任何事件的处理。它在初始化程序中设置为 False,并且只能由日志记录配置代码更改。

注意

此属性应视为只读。

setLevel(level)

将此记录器的阈值设置为 *level*。严重程度低于 *level* 的日志消息将被忽略;严重程度为 *level* 或更高的日志消息将由服务于此记录器的任何处理程序发出,除非处理程序的级别已设置为比 *level* 更高的严重程度级别。

创建记录器时,级别设置为 NOTSET(当记录器是根记录器时,这会导致处理所有消息,或者当记录器是非根记录器时,委托给父记录器)。请注意,根记录器是在级别 WARNING 创建的。

术语“委托给父级”意味着如果记录器的级别为 NOTSET,则会遍历其祖先记录器链,直到找到级别不是 NOTSET 的祖先,或者到达根记录器。

如果找到级别不是 NOTSET 的祖先,则该祖先的级别将被视为祖先搜索开始处的记录器的有效级别,并用于确定如何处理日志事件。

如果到达根记录器,并且其级别为 NOTSET,则将处理所有消息。否则,将使用根记录器的级别作为有效级别。

有关级别列表,请参阅 日志记录级别

在 3.2 版更改: *level* 参数现在接受级别的字符串表示形式(例如“INFO”)作为整数常量(例如 INFO)的替代形式。但是请注意,级别在内部存储为整数,并且诸如 getEffectiveLevel()isEnabledFor() 之类的方法将返回/期望传递整数。

isEnabledFor(level)

指示此记录器是否会处理严重程度为 *level* 的消息。此方法首先检查由 logging.disable(level) 设置的模块级级别,然后检查由 getEffectiveLevel() 确定的记录器的有效级别。

getEffectiveLevel()

指示此记录器的有效级别。如果使用 setLevel() 设置了 NOTSET 以外的值,则返回该值。否则,将朝根记录器遍历层次结构,直到找到 NOTSET 以外的值,并返回该值。返回的值是一个整数,通常是 logging.DEBUGlogging.INFO 等之一。

getChild(suffix)

返回一个记录器,该记录器是此记录器的后代,由后缀确定。因此,logging.getLogger('abc').getChild('def.ghi') 将返回与 logging.getLogger('abc.def.ghi') 返回的记录器相同的记录器。这是一种便捷方法,当使用例如 __name__ 而不是文字字符串命名父记录器时非常有用。

3.2 版新增。

getChildren()

返回一组记录器,这些记录器是此记录器的直接子级。例如,logging.getLogger().getChildren() 可能会返回一个包含名为 foobar 的记录器的集合,但名为 foo.bar 的记录器不会包含在该集合中。同样,logging.getLogger('foo').getChildren() 可能会返回一个包含名为 foo.bar 的记录器的集合,但不会包含名为 foo.bar.baz 的记录器。

3.12 版新增。

debug(msg, *args, **kwargs)

使用级别 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。如果为 true,则将堆栈信息添加到日志消息中,包括实际的日志记录调用。请注意,这与通过指定 *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.propagate 属性)未附加任何处理程序,则消息将发送到 lastResort 上设置的处理程序。

在 3.2 版更改: 添加了 *stack_info* 参数。

在 3.5 版更改: *exc_info* 参数现在可以接受异常实例。

在 3.8 版更改: 添加了 *stacklevel* 参数。

info(msg, *args, **kwargs)

使用级别 INFO 在此记录器上记录消息。参数的解释与 debug() 相同。

warning(msg, *args, **kwargs)

使用级别 WARNING 在此记录器上记录消息。参数的解释与 debug() 相同。

注意

有一个已过时的函数 warn,其功能与 warning 相同。由于 warn 已弃用,请不要使用它 - 请改用 warning

error(msg, *args, **kwargs)

使用级别 ERROR 在此记录器上记录消息。参数的解释与 debug() 相同。

critical(msg, *args, **kwargs)

使用级别 CRITICAL 在此记录器上记录消息。参数的解释与 debug() 相同。

log(level, msg, *args, **kwargs)

使用整数级别 *level* 在此记录器上记录消息。其他参数的解释与 debug() 相同。

exception(msg, *args, **kwargs)

使用级别 ERROR 在此记录器上记录消息。参数的解释与 debug() 相同。异常信息将添加到日志消息中。此函数应该只从异常处理程序中调用。

addFilter(filter)

将指定的过滤器 *filter* 添加到此记录器。

removeFilter(filter)

从此记录器中删除指定的过滤器 *filter*。

filter(record)

将此记录器的过滤器应用于记录,如果要处理记录,则返回 True。依次查询过滤器,直到其中一个返回假值。如果它们都没有返回假值,则将处理记录(传递给处理程序)。如果一个返回假值,则不会对记录进行进一步处理。

addHandler(hdlr)

将指定的处理程序 *hdlr* 添加到此记录器。

removeHandler(hdlr)

从此记录器中删除指定的处理程序 *hdlr*。

findCaller(stack_info=False, stacklevel=1)

查找调用方的源文件名和行号。以 4 元素元组的形式返回文件名、行号、函数名和堆栈信息。除非 *stack_info* 为 True,否则堆栈信息将返回为 None

*stacklevel* 参数从调用 debug() 和其他 API 的代码传递。如果大于 1,则使用超出部分在确定要返回的值之前跳过堆栈帧。当从辅助/包装器代码调用日志记录 API 时,这通常很有用,以便事件日志中的信息不是指辅助/包装器代码,而是指调用它的代码。

handle(record)

通过将记录传递给与此记录器及其祖先关联的所有处理程序(直到找到 propagate 的假值)来处理记录。此方法用于处理从套接字接收到的以及在本地创建的未被 pickle 的记录。记录器级别的过滤使用 filter() 应用。

makeRecord(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None)

这是一个工厂方法,可以在子类中重写它以创建专门的 LogRecord 实例。

hasHandlers()

检查此记录器是否配置了任何处理程序。这是通过在此记录器及其父记录器(在记录器层次结构中)中查找处理程序来完成的。如果找到处理程序,则返回 True,否则返回 False。只要找到“propagate”属性设置为 false 的记录器,该方法就会停止向上搜索层次结构 - 这将是最后一个检查是否存在处理程序的记录器。

3.2 版新增。

在 3.7 版更改: 记录器现在可以被 pickle 和 unpickle。

日志级别

日志级别的数值在下表中给出。如果您想定义自己的级别,并且需要它们相对于预定义级别具有特定的值,那么这些级别主要对您有用。如果您定义的级别的数值相同,则它将覆盖预定义的值;预定义的名称将丢失。

级别

数值

它的含义/何时使用它

logging.NOTSET

0

当在记录器上设置时,表示要咨询祖先记录器以确定有效级别。如果仍然解析为 NOTSET,则记录所有事件。当在处理程序上设置时,将处理所有事件。

logging.DEBUG

10

详细信息,通常只对试图诊断问题的开发人员有用。

logging.INFO

20

确认一切按预期进行。

logging.WARNING

30

指示发生了意外情况,或者在不久的将来可能会出现问题(例如“磁盘空间不足”)。该软件仍在按预期工作。

logging.ERROR

40

由于更严重的问题,软件无法执行某些功能。

logging.CRITICAL

50

严重错误,表示程序本身可能无法继续运行。

处理程序对象

处理程序具有以下属性和方法。请注意,Handler 从未直接实例化;此类充当更有用的子类的基类。但是,子类中的 __init__() 方法需要调用 Handler.__init__()

class logging.Handler
__init__(level=NOTSET)

通过设置其级别、将过滤器列表设置为空列表并创建一个锁(使用 createLock())来序列化对 I/O 机制的访问,从而初始化 Handler 实例。

createLock()

初始化一个线程锁,该锁可用于序列化对可能不是线程安全的底层 I/O 功能的访问。

acquire()

获取使用 createLock() 创建的线程锁。

release()

释放使用 acquire() 获取的线程锁。

setLevel(level)

将此处理程序的阈值设置为 level。严重程度低于 level 的日志消息将被忽略。创建处理程序时,级别设置为 NOTSET(这将导致处理所有消息)。

有关级别列表,请参阅 日志记录级别

在 3.2 版更改: level 参数现在接受级别的字符串表示形式(例如“INFO”)作为整数常量(例如 INFO)的替代形式。

setFormatter(fmt)

将此处理程序的 Formatter 设置为 fmt

addFilter(filter)

将指定的过滤器 filter 添加到此处理程序。

removeFilter(filter)

从此处理程序中删除指定的过滤器 filter

filter(record)

将此处理程序的过滤器应用于记录,如果要处理记录,则返回 True。依次查询过滤器,直到其中一个返回假值。如果它们都没有返回假值,则将发出记录。如果一个返回假值,则处理程序将不会发出记录。

flush()

确保已刷新所有日志输出。此版本不执行任何操作,旨在由子类实现。

close()

清理处理程序使用的所有资源。此版本不执行任何输出,但会从处理程序的内部列表中移除该处理程序,该列表在调用 shutdown() 时关闭。子类应确保从重写的 close() 方法中调用此方法。

handle(record)

根据可能已添加到处理程序的过滤器,有条件地发出指定的日志记录。使用获取/释放 I/O 线程锁来包装记录的实际发出。

handleError(record)

当在 emit() 调用期间遇到异常时,应从处理程序调用此方法。如果模块级属性 raiseExceptionsFalse,则异常会被静默忽略。这通常是日志系统所期望的 - 大多数用户不关心日志系统中的错误,他们更感兴趣的是应用程序错误。但是,如果您愿意,可以用自定义处理程序替换它。指定的记录是在发生异常时正在处理的记录。(raiseExceptions 的默认值为 True,因为这在开发过程中更有用)。

format(record)

对记录进行格式化 - 如果设置了格式化程序,则使用它。否则,使用模块的默认格式化程序。

emit(record)

执行实际记录指定日志记录所需的操作。此版本旨在由子类实现,因此会引发 NotImplementedError

警告

此方法在获取处理程序级锁后调用,该锁在此方法返回后释放。当您重写此方法时,请注意在调用任何调用日志记录 API 其他部分(可能会进行锁定)的内容时要小心,因为这可能会导致死锁。特别是

  • 日志记录配置 API 会获取模块级锁,然后在配置这些处理程序时获取各个处理程序级锁。

  • 许多日志记录 API 会锁定模块级锁。如果从此方法调用此类 API,则如果在另一个线程上进行配置调用,则可能会导致死锁,因为该线程将尝试在获取处理程序级锁之前获取模块级锁,而此线程尝试在获取处理程序级锁之后获取模块级锁(因为在此方法中,已经获取了处理程序级锁)。

有关作为标准包含的处理程序列表,请参阅 logging.handlers

格式化程序对象

class logging.Formatter(fmt=None, datefmt=None, style='%', validate=True, *, defaults=None)

负责将 LogRecord 转换为要由人或外部系统解释的输出字符串。

参数:
  • fmt (str) – 以给定 *style* 表示的整个日志输出的格式字符串。可能的映射键来自 LogRecord 对象的 LogRecord 属性。如果未指定,则使用 '%(message)s',它只是记录的消息。

  • datefmt (str) – 以给定 *style* 表示的日志输出日期/时间部分的格式字符串。如果未指定,则使用 formatTime() 中描述的默认值。

  • style (str) – 可以是 '%''{''$' 之一,并确定格式字符串如何与其数据合并:使用 printf 风格的字符串格式化 (%)、str.format() ({) 或 string.Template ($)。这仅适用于 *fmt* 和 *datefmt*(例如 '%(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)

记录的属性字典用作字符串格式化操作的操作数。返回结果字符串。在格式化字典之前,要执行几个准备步骤。使用 msg % args 计算记录的 message 属性。如果格式字符串包含 '(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)

返回记录列表的标题。基本实现只返回空字符串。如果您需要特定的行为,例如显示记录计数、标题或分隔线,则需要覆盖此方法。

formatFooter(records)

返回记录列表的页脚。基本实现只返回空字符串。如果您需要特定的行为,例如显示记录计数或分隔线,则需要覆盖此方法。

format(records)

返回记录列表的格式化文本。基本实现只在没有记录时返回空字符串;否则,它返回标题、使用行格式化程序格式化的每个记录以及页脚的串联。

过滤器对象

Filters 可以由 HandlersLoggers 用于比级别提供的更复杂的过滤。基本过滤器类只允许低于记录器层次结构中某个点的事件。例如,使用“A.B”初始化的过滤器将允许由记录器“A.B”、“A.B.C”、“A.B.C.D”、“A.B.D”等记录的事件,但不允许“A.BB”、“B.A.B”等。如果使用空字符串初始化,则所有事件都将被传递。

class logging.Filter(name='')

返回 Filter 类的一个实例。如果指定了 *name*,它将命名一个记录器,该记录器及其子记录器的事件将被允许通过过滤器。如果 *name* 是空字符串,则允许所有事件。

filter(record)

指定的记录是否要被记录?返回 false 表示否,true 表示是。过滤器可以就地修改日志记录,也可以返回一个完全不同的记录实例,该实例将在事件的任何后续处理中替换原始日志记录。

请注意,附加到处理程序的过滤器会在处理程序发出事件之前被查询,而附加到记录器的过滤器则在每次记录事件时(使用 debug()info() 等)在将事件发送到处理程序之前被查询。这意味着由后代记录器生成的事件不会被记录器的过滤器设置过滤,除非过滤器也被应用于这些后代记录器。

您实际上不需要子类化 Filter:您可以传递任何具有语义相同的 filter 方法的实例。

在 3.2 版更改: 您不需要创建专门的 Filter 类,也不需要使用其他具有 filter 方法的类:您可以使用函数(或其他可调用对象)作为过滤器。过滤逻辑将检查过滤器对象是否具有 filter 属性:如果有,则假定它是 Filter,并调用其 filter() 方法。否则,假定它是可调用对象,并以记录作为单个参数调用。返回值应符合 filter() 返回的值。

在 3.12 版更改: 您现在可以从过滤器返回一个 LogRecord 实例来替换日志记录,而不是就地修改它。这允许附加到 Handler 的过滤器在发出日志记录之前对其进行修改,而不会对其他处理程序产生副作用。

尽管过滤器主要用于根据比级别更复杂的条件过滤记录,但它们会看到由它们所附加的处理程序或记录器处理的每条记录:如果您想执行诸如计算特定记录器或处理程序处理了多少条记录,或者在正在处理的 LogRecord 中添加、更改或删除属性等操作,这将非常有用。显然,更改 LogRecord 需要谨慎操作,但它确实允许将上下文信息注入日志(请参阅 使用过滤器传递上下文信息)。

LogRecord 对象

每次记录某些内容时,Logger 都会自动创建 LogRecord 实例,并且可以通过 makeLogRecord() 手动创建(例如,从通过网络接收的已腌制的事件)。

class logging.LogRecord(name, level, pathname, lineno, msg, args, exc_info, func=None, sinfo=None)

包含与正在记录的事件相关的所有信息。

主要信息在 *msg* 和 *args* 中传递,它们使用 msg % args 组合以创建记录的 message 属性。

参数:
  • **name** (str) – 用于记录此 LogRecord 所代表事件的记录器的名称。请注意,LogRecord 中的记录器名称将始终具有此值,即使它是由附加到不同(祖先)记录器的处理程序发出的。

  • **level** (int) – 日志事件的 数字级别(例如 DEBUG10INFO20 等)。请注意,这将转换为 LogRecord 的 *两个* 属性:levelno 用于数字值,levelname 用于相应的级别名称。

  • **pathname** (str) – 进行日志记录调用的源文件的完整字符串路径。

  • **lineno** (int) – 进行日志记录调用的源文件中的行号。

  • **msg** (Any) – 事件描述消息,可以是带有变量数据占位符的 %-格式字符串,也可以是任意对象(请参阅 使用任意对象作为消息)。

  • **args** (tuple | dict[str, Any]) – 要合并到 *msg* 参数中的变量数据,以获取事件描述。

  • **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 属性之间的名称并不总是完全对应。)这些属性可用于将记录中的数据合并到格式字符串中。下表按字母顺序列出了属性名称、它们的含义以及 %-style 格式字符串中相应的占位符。

如果您使用 {} 格式化(str.format()),则可以使用 {attrname} 作为格式字符串中的占位符。如果您使用 $-formatting(string.Template),请使用 ${attrname} 形式。当然,在这两种情况下,都将 attrname 替换为您要使用的实际属性名称。

在 {} 格式化的情况下,您可以通过将格式化标志放在属性名称后面,并用冒号分隔来指定格式化标志。例如:占位符 {msecs:03.0f} 会将毫秒值 4 格式化为 004。有关可用选项的完整详细信息,请参阅 str.format() 文档。

属性名称

格式

描述

args

您不需要自己格式化此项。

合并到 msg 中以生成 message 的参数元组,或者其值用于合并的字典(当只有一个参数且它是一个字典时)。

asctime

%(asctime)s

创建 LogRecord 时的人类可读时间。默认情况下,格式为“2003-07-08 16:49:45,896”(逗号后的数字是时间的毫秒部分)。

created

%(created)f

创建 LogRecord 的时间(由 time.time() 返回)。

exc_info

您不需要自己格式化此项。

异常元组(类似于 sys.exc_info),或者,如果没有发生异常,则为 None

filename

%(filename)s

pathname 的文件名部分。

funcName

%(funcName)s

包含日志调用的函数的名称。

levelname

%(levelname)s

消息的文本日志级别('DEBUG''INFO''WARNING''ERROR''CRITICAL')。

levelno

%(levelno)s

消息的数字日志级别(DEBUGINFOWARNINGERRORCRITICAL)。

lineno

%(lineno)d

发出日志调用的源代码行号(如果可用)。

message

%(message)s

记录的消息,计算为 msg % args。这是在调用 Formatter.format() 时设置的。

module

%(module)s

模块(filename 的名称部分)。

msecs

%(msecs)d

创建 LogRecord 时的毫秒部分。

msg

您不需要自己格式化此项。

在原始日志调用中传递的格式字符串。与 args 合并以生成 message,或任意对象(请参阅 使用任意对象作为消息)。

name

%(name)s

用于记录调用的记录器的名称。

pathname

%(pathname)s

发出日志调用的源文件的完整路径名(如果可用)。

process

%(process)d

进程 ID(如果可用)。

processName

%(processName)s

进程名称(如果可用)。

relativeCreated

%(relativeCreated)d

创建 LogRecord 时的毫秒数,相对于加载日志记录模块的时间。

stack_info

您不需要自己格式化此项。

从当前线程中堆栈底部到导致创建此记录的日志调用的堆栈帧(包括该帧)的堆栈帧信息(如果可用)。

thread

%(thread)d

线程 ID(如果可用)。

threadName

%(threadName)s

线程名称(如果可用)。

taskName

%(taskName)s

asyncio.Task 名称(如果可用)。

版本 3.1 中的变化: 添加了 processName

版本 3.12 中的变化: 添加了 taskName

LoggerAdapter 对象

LoggerAdapter 实例用于方便地将上下文信息传递到日志调用中。有关使用示例,请参阅关于 将上下文信息添加到日志输出中 的部分。

class logging.LoggerAdapter(logger, extra)

返回 LoggerAdapter 的实例,该实例使用底层 Logger 实例和一个类字典对象进行初始化。

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 版更改: 已将 isEnabledFor()getEffectiveLevel()setLevel()hasHandlers() 方法添加到 LoggerAdapter。这些方法委托给底层记录器。

在 3.6 版更改: 添加了属性 manager 和方法 _log(),它们委托给底层记录器并允许嵌套适配器。

线程安全

日志记录模块旨在实现线程安全,而无需其客户端执行任何特殊工作。它通过使用线程锁来实现这一点;有一个锁用于序列化对模块共享数据的访问,并且每个处理程序还会创建一个锁来序列化对其底层 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.info(msg, *args, **kwargs)

在根记录器上记录级别为 INFO 的消息。参数和行为与 debug() 相同。

logging.warning(msg, *args, **kwargs)

在根记录器上记录级别为 WARNING 的消息。参数和行为与 debug() 相同。

注意

有一个已过时的函数 warn,它的功能与 warning 相同。由于 warn 已弃用,请不要使用它,而应使用 warning

logging.error(msg, *args, **kwargs)

在根记录器上记录级别为 ERROR 的消息。参数和行为与 debug() 相同。

logging.critical(msg, *args, **kwargs)

在根记录器上记录级别为 CRITICAL 的消息。参数和行为与 debug() 相同。

logging.exception(msg, *args, **kwargs)

在根记录器上记录级别为 ERROR 的消息。参数和行为与 debug() 相同。异常信息将添加到日志消息中。此函数只能从异常处理程序中调用。

logging.log(level, msg, *args, **kwargs)

在根记录器上记录级别为 *level* 的消息。参数和行为与 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* 是预定义级别 CRITICALERRORWARNINGINFODEBUG 之一,则返回相应的字符串。如果已使用 addLevelName() 将级别与名称相关联,则返回与 *level* 关联的名称。如果传入与其中一个定义级别相对应的数值,则返回相应的字符串表示形式。

*level* 参数也接受级别的字符串表示形式,例如“INFO”。在这种情况下,此函数返回该级别的相应数值。

如果没有传入匹配的数值或字符串值,则返回字符串“Level %s” % level。

注意

级别在内部是整数(因为需要在日志记录逻辑中进行比较)。此函数用于在整数级别和通过 %(levelname)s 格式说明符(请参阅LogRecord 属性)在格式化日志输出中显示的级别名称之间进行转换,反之亦然。

在 3.4 版更改: 在 3.4 之前的 Python 版本中,此函数也可以传递文本级别,并返回该级别的相应数值。这种未记录的行为被认为是一个错误,并在 Python 3.4 中被删除,但在 3.4.2 中为了保持向后兼容性而恢复。

logging.getHandlerByName(name)

返回具有指定 *name* 的处理器,如果不存在具有该名称的处理器,则返回 None

3.12 版新增。

logging.getHandlerNames()

返回所有已知处理器名称的不可变集合。

3.12 版新增。

logging.makeLogRecord(attrdict)

创建并返回一个新的 LogRecord 实例,其属性由 *attrdict* 定义。此函数对于获取通过套接字发送的已腌制的 LogRecord 属性字典,并在接收端将其重构为 LogRecord 实例非常有用。

logging.basicConfig(**kwargs)

通过使用默认的 Formatter 创建 StreamHandler 并将其添加到根记录器,对日志记录系统进行基本配置。如果未为根记录器定义任何处理器,则函数 debug()info()warning()error()critical() 将自动调用 basicConfig()

如果根记录器已经配置了处理器,则此函数不执行任何操作,除非关键字参数 *force* 设置为 True

注意

应在启动其他线程之前从主线程调用此函数。在 2.7.1 和 3.2 之前的 Python 版本中,如果从多个线程调用此函数,则有可能(在极少数情况下)将处理器多次添加到根记录器,从而导致意外结果,例如消息在日志中重复。

支持以下关键字参数。

格式

描述

filename

filename

指定创建 FileHandler(使用指定的 filename),而不是 StreamHandler

filemode

如果指定了 *filename*,则以这种 模式 打开文件。默认为 'a'

format

对处理器使用指定的格式字符串。默认为属性 levelnamenamemessage,用冒号分隔。

datefmt

使用指定的日期/时间格式,如 time.strftime() 所接受。

style

如果指定了 *format*,则对格式字符串使用此样式。对于 printf 样式str.format()string.Template,分别为 '%''{''$'。默认为 '%'

level

将根记录器级别设置为指定的 级别

stream

使用指定的流初始化 StreamHandler。请注意,此参数与 *filename* 不兼容 - 如果两者都存在,则会引发 ValueError

如果指定,这应该是一个已创建的处理程序的可迭代对象,用于添加到根记录器。任何尚未设置格式化程序的处理程序都将被分配在此函数中创建的默认格式化程序。请注意,此参数与 *filename* 或 *stream* 不兼容 - 如果两者都存在,则会引发 ValueError

force

如果此关键字参数指定为 true,则在根据其他参数执行配置之前,将删除并关闭附加到根记录器的任何现有处理程序。

encoding

如果此关键字参数与 *filename* 一起指定,则在创建 FileHandler 时将使用其值,因此在打开输出文件时也会使用该值。

errors

如果此关键字参数与 *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__(),以便只需要一个名称参数,并且 __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

可以通过此属性获得“最后手段处理程序”。这是一个写入 sys.stderrStreamHandler,级别为 WARNING,用于在没有任何日志记录配置的情况下处理日志记录事件。最终结果是仅将消息打印到 sys.stderr。这取代了之前的错误消息,即“找不到记录器 XYZ 的处理程序”。如果出于某种原因需要之前的行为,可以将 lastResort 设置为 None

3.2 版新增。

logging.raiseExceptions

用于查看是否应传播处理期间的异常。

默认值:True

如果 raiseExceptionsFalse,则异常会被静默忽略。这对于日志记录系统来说是大多数情况下想要的 - 大多数用户不会关心日志记录系统中的错误,他们更关心应用程序错误。

与警告模块的集成

captureWarnings() 函数可用于将 loggingwarnings 模块集成。

logging.captureWarnings(capture)

此函数用于打开和关闭通过日志记录捕获警告的功能。

如果 captureTrue,则 warnings 模块发出的警告将被重定向到日志记录系统。具体来说,将使用 warnings.formatwarning() 格式化警告,并将结果字符串记录到名为 'py.warnings' 的记录器,其严重级别为 WARNING

如果 captureFalse,则警告到日志记录系统的重定向将停止,并且警告将被重定向到其原始目标(即在调用 captureWarnings(True) 之前生效的目标)。

另请参阅

模块 logging.config

日志记录模块的配置 API。

模块 logging.handlers

日志记录模块包含的有用处理程序。

PEP 282 - 日志记录系统

描述此功能以包含在 Python 标准库中的提案。

原始 Python 日志记录包

这是 logging 包的原始来源。此站点提供的软件包版本适用于 Python 1.5.2、2.1.x 和 2.2.x,这些版本在标准库中不包含 logging 包。