"logging" --- Python 的日志记录工具
***********************************

**源代码：** Lib/logging/__init__.py


Important
^^^^^^^^^

此页面仅包含 API 参考信息。教程信息和更多高级用法的讨论，请参阅

* 基础教程

* 进阶教程

* 日志记录操作手册

======================================================================

这个模块为应用与库实现了灵活的事件日志系统的函数与类。

使用标准库提供的 logging 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 不太熟悉， 掌握它最好
的方式就是查看它对应的教程（**详见右侧的链接**）。

该模块定义的基础类，以及它们的属性和方法都在下面的小节中列出。

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

* 处理器将日志记录（由记录器创建）发送到适当的目标。

* 过滤器提供了更细粒度的功能，用于确定要输出的日志记录。

* 格式器指定最终输出中日志记录的样式。


记录器对象
==========

记录器有以下的属性和方法。注意 *永远* 不要直接实例化记录器，应当通过模
块级别的函数 "logging.getLogger(name)" 。多次使用相同的名字调用
"getLogger()" 会一直返回相同的 Logger 对象的引用。

"name" 一般是以句点分割的层级值，如 "foo.bar.baz" (尽管也可以是简单形
式，例如 "foo")。 层级结构列表中位于下方的日志记录器是列表中较高位置的
日志记录器的子级。 例如，假定有一个名叫 "foo" 的日志记录器，则名字为
"foo.bar", "foo.bar.baz" 和 "foo.bam" 的日志记录器都是 "foo" 的子级。
而且，所有日志记录器都是根日志记录器的子级。 日志记录器名称的层级结构
类似于 Python 包的层级结构，如果你使用建议的构造
"logging.getLogger(__name__)" 以每个模块为基础来组织你的日志记录器则将
与后者完全一致。 这是因为在一个模块中，"__name__" 是该模块在 Python 包
命名空间中的名字。

class logging.Logger

   name

      这是日志记录器的名称，也是传给 "getLogger()" 用以获取日志记录器
      的值。

      备注:

        该属性应当被视为是只读的。

   level

      该日志记录器的阈值，由 "setLevel()" 方法设置。

      备注:

        请不要直接设置该值 —— 应当始终使用 "setLevel()"，它会检查传入
        的级别。

   parent

      此日志记录器的父日志记录器。它可能会根据命名空间层次结构中更高日
      志记录器的实例化而发生变化。

      备注:

        该值应被视为只读 。

   propagate

      如果这个属性为真，记录到这个记录器的事件除了会发送到此记录器关联
      的所有处理器外，还会传递给更高级别（祖先）记录器的处理器。消息会
      直接传递给祖先记录器的处理器 —— 不考虑祖先记录器的级别和过滤器。

      如果为假，记录消息将不会传递给当前记录器的祖先记录器的处理器。

      举例说明：如果名为 "A.B.C" 的记录器的传播属性求值为真，则任何通
      过调用诸如 "logging.getLogger('A.B.C').error(...)" 之类的方法记
      录到 "A.B.C" 的事件，在第一次被传递到 "A.B.C" 上附加的处理器后，
      将[取决于传递该记录器的级别和过滤器设置]依次传递给附加到名为
      "A.B"，"A" 的记录器和根记录器的所有处理器。如果 "A.B.C"、"A.B"、
      "A" 组成的链中，任一记录器的 "propagate" 属性设置为假，那么这将
      是最后一个其处理器会收到事件的记录器，此后传播在该点停止。

      构造器将这个属性初始化为 "True"。

      备注:

        如果你将一个处理器附加到一个记录器 *和* 其一个或多个祖先记录器
        ，它可能发出多次相同的记录。通常，您不需要将一个处理器附加到一
        个以上的记录器上 —— 如果您将它附加到记录器层次结构中最高的适当
        记录器上，则它将看到所有后代记录器记录的所有事件，前提是它们的
        传播设置保留为 "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.DEBUG"、 "logging.INFO" 等等。

   getChild(suffix)

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

      Added in version 3.2.

   getChildren()

      返回由该日志记录器的直接下级日志记录器组成的集合。 举例来说
      "logging.getLogger().getChildren()" 将返回包含名为 "foo" 和
      "bar" 的日志记录器的集合，但名为 "foo.bar" 的日志记录器则不会包
      括在集合中。 类似地，"logging.getLogger('foo').getChildren()" 将
      返回包括名为 "foo.bar" 的日志记录器的集合，但不会包括名为
      "foo.bar.baz" 的日志记录器。

      Added in version 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()"。

      备注:

        有一个功能上与 "warning" 一致的方法 "warn"。由于 "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
      元素元组的形式返回。堆栈信息将返回 "None"，除非 *stack_info* 为
      "True"。

      *stacklevel* 参数用于调用 "debug()" 和其他 API。如果大于 1，则多
      余部分将用于跳过堆栈帧，然后再确定要返回的值。当从帮助器/包装器
      代码调用日志记录 API 时，这通常很有用，以便事件日志中的信息不是
      来自帮助器/包装器代码，而是来自调用它的代码。

   handle(record)

      通过将记录传递给与此记录器及其祖先关联的所有处理器来处理（直到某
      个 *propagate* 值为 false）。此方法用于从套接字接收的未序列化的
      以及在本地创建的记录。使用 "filter()" 进行记录器级别过滤。

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

      这是一种工厂方法，可以在子类中对其进行重写以创建专门的
      "LogRecord" 实例。

   hasHandlers()

      检查此记录器是否配置了任何处理器。通过在此记录器及其记录器层次结
      构中的父级中查找处理器完成此操作。如果找到处理器则返回 "True"，
      否则返回 "False"。只要找到 “propagate” 属性设置为假值的记录器，
      该方法就会停止搜索层次结构 —— 其将是最后一个检查处理器是否存在的
      记录器。

      Added in version 3.2.

   在 3.7 版本发生变更: 现在可以对日志记录器进行序列化和反序列化。


日志级别
========

日志记录级别的数值在下表中给出。如果你想要定义自己的级别，并且需要它们
具有相对于预定义级别的特定值，那么这你可能对以下内容感兴趣。如果你定义
具有相同数值的级别，它将覆盖预定义的值；预定义的名称将失效。

+-------------------------+-----------------+---------------------------------------+
| 级别                    | 数值            | 何种含义 / 何时使用                   |
|=========================|=================|=======================================|
| 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)

      初始化 "Handler" 实例时，需要设置它的级别，将过滤列表置为空，并
      且创建锁（通过 "createLock()" ）来序列化对 I/O 的访问。

   createLock()

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

   acquire()

      获取由 "createLock()" 创建的线程锁。

   release()

      释放由 "acquire()" 获取的线程锁。

   setLevel(level)

      给处理器设置阈值为 *level* 。日志级别小于 *level* 将被忽略。创建
      处理器时，日志级别被设置为 "NOTSET" （所有的消息都会被处理）。

      参见 日志级别 级别列表。

      在 3.2 版本发生变更: *level* 形参现在接受像 'INFO' 这样的字符串
      形式的级别表达方式，也可以使用像 "INFO" 这样的整数常量。

   setFormatter(fmt)

      将处理器的格式设为 *fmt*。 *fmt* 参数必须为 "Formatter" 实例或
      "None"。

   addFilter(filter)

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

   removeFilter(filter)

      从此处理器中删除指定的过滤器 *filter* 。

   filter(record)

      将此处理器的过滤器应用于记录，在要处理记录时返回 "True" 。依次查
      询过滤器，直到其中一个返回假值为止。如果它们都不返回假值，则将发
      出记录。如果返回一个假值，则处理器将不会发出记录。

   flush()

      确保所有日志记录从缓存输出。此版本不执行任何操作，并且应由子类实
      现。

   close()

      清理处理器使用的所有资源。 此版本没有输出但会从内部处理器映射中
      移除处理器，该映射被用来按名称查找处理器。

      子类应当通过重写 "close()" 方法确保它会被调用。

   handle(record)

      经已添加到处理器的过滤器过滤后，有条件地发出指定的日志记录。用获
      取/释放 I/O 线程锁包装了记录的实际发出行为。

   handleError(record)

      此方法应当在 "emit()" 调用期间遇到异常时从处理器中调用。 如果模
      块级属性 "raiseExceptions" 为 "False"，则异常将被静默地忽略。 这
      是大多数情况下日志系统所需要的 —— 大多数用户不会关心日志系统中的
      错误，他们对应用程序错误更感兴趣。 但是，你可以根据需要将其替换
      为自定义处理器。 指定的记录是发生异常时正在处理的记录。
      ("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*) -- 用于日志记录输出的日期/时间部分的格式字
        符串。 如果未指定，则将使用 "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* 属性中。 这很有用因为异常信息可
      以被 pickle 并通过网络发送，但是如果你有不止一个对异常信息进行定
      制的 "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* 列表返回一个标头。 基本实现只是返回空字符串。 如果
      你想要指明特定行为则需要重写此方法，例如显示记录条数、标题或分隔
      行等。

   formatFooter(records)

      为 *records* 列表返回一个结束标记。 基本实现只是返回空字符串。
      如果你想要指明特定行为则需要重写此方法，例如显示记录条数或分隔行
      等。

   format(records)

      为 *records* 列表返回已格式化文本。 基本实现在没有记录时只是返回
      空字符串；在其他情况下，它将返回标头、使用行格式化器执行格式化的
      每行记录以及结束标记。


过滤器对象
==========

"Filters" 可被 "Handlers" 和 "Loggers" 用来实现比按层级提供更复杂的过
滤操作。 基本过滤器类只允许低于日志记录器层级结构中低于特定层级的事件
。 例如，一个用 '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)

      指定的记录是否会被写入日志？否则返回假值，是则返回真值。 过滤器
      可以原地修改日志记录或者返回完全不同的记录实例并在该事件未来的任
      何处理过程中用它来替代原始日志记录。

请注意关联到处理器的过滤器会在事件由处理器发出之前被查询，而关联到日志
记录器的过滤器则会在有事件被记录的任何时候（使用 "debug()", "info()"
等等）在将事件发送给处理器之前被查询。 这意味着由后代日志记录器生成的
事件将不会被父代日志记录器的过滤器设置所过滤，除非该过滤器也已被应用于
后代日志记录器。

你实际上不需要子类化 "Filter" ：你可以传入任何一个包含有相同语义的
"filter" 方法的实例。

在 3.2 版本发生变更: 你不需要创建专门的 "Filter" 类，或使用具有
"filter" 方法的其他类：你可以使用一个函数（或其他可调用对象）作为过滤
器。 过滤逻辑将检查过滤器对象是否具有 "filter" 属性：如果有，就会将它
当作是 "Filter" 并调用它的 "filter()" 方法。 在其他情况下，则会将它当
作是可调用对象并将记录作为唯一的形参进行调用。 返回值应当与 "filter()"
的返回值相一致。

在 3.12 版本发生变更: 现在你可以从过滤器返回一个 "LogRecord" 实例来替
代日志记录而不是原地修改它。 这允许附加到特定 "Handler" 的过滤器在日志
记录发出之前修改它，而不会对其他处理器产生附带影响。

尽管过滤器主要被用来构造比层级更复杂的规则以过滤记录，但它们可以查看由
它们关联的处理器或记录器所处理的每条记录：当你想要执行统计特定记录器或
处理器共处理了多少条记录，或是在所处理的 "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" 所表示事件的记录器
        名称。 请注意 "LogRecord" 中的记录器名称将始终为该值，即使它可
        能是由附加到不同（上级）日志记录器的处理器所发出的。

      * **level** (*int*) -- 日志记录事件的 数字层级 (如 "10" 表示
        "DEBUG", "20" 表示 "INFO" 等等)。 请注意这会转换为 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}" 用作格式
字符串内的占位符。 如果是使用 $-格式化（"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_ns()" / 1e9 所返回的值）。           |
+------------------+---------------------------+-------------------------------------------------+
| exc_info         | 此属性不需要用户进行格式  | 异常元组（例如 "sys.exc_info"）或者如未发生异常 |
|                  | 化。                      | 则为 "None"。                                   |
+------------------+---------------------------+-------------------------------------------------+
| exc_text         | 此属性不需要用户进行格式  | 格式化为字符串的异常信息。 这是在               |
|                  | 化。                      | "Formatter.format()" 被唤起时设置的 ，或者如果  |
|                  |                           | 未发生异常则为 "None"。                         |
+------------------+---------------------------+-------------------------------------------------+
| filename         | "%(filename)s"            | "pathname" 的文件名部分。                       |
+------------------+---------------------------+-------------------------------------------------+
| funcName         | "%(funcName)s"            | 包含日志记录调用的函数名。                      |
+------------------+---------------------------+-------------------------------------------------+
| levelname        | "%(levelname)s"           | 消息文本记录级别（"'DEBUG'"，"'INFO'"，         |
|                  |                           | "'WARNING'"，"'ERROR'"， "'CRITICAL'"）。       |
+------------------+---------------------------+-------------------------------------------------+
| levelno          | "%(levelno)s"             | 消息数字的记录级别 ("DEBUG", "INFO", "WARNING", |
|                  |                           | "ERROR", "CRITICAL").                           |
+------------------+---------------------------+-------------------------------------------------+
| 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 被创建的时间，即相对于 |
|                  |                           | logging 模块被加载时间 的差值。                 |
+------------------+---------------------------+-------------------------------------------------+
| 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=None, merge_extra=False)

   返回一个使用下层 "Logger" 实例、一个可选的字典型对象 (*extra*) 和一
   个可选的指明单独日志调用的 *extra* 参数是否要与 "LoggerAdapter" 的
   extra 值合并的布尔值 (*merge_extra*) 进行初始化的 "LoggerAdapter"
   实例。 其默认行为将忽略单独日志调用的 *extra* 参数并只使用
   "LoggerAdapter" 实例中的参数值。

   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()" 方法，它们会
   委托给下层的日志记录器并允许适配器嵌套。

   在 3.10 版本发生变更: 现在 *extra* 参数是可选的。

   在 3.13 版本发生变更: 增加了 *merge_extra* 形参。


线程安全
========

logging 模块的目标是在客户端无需执行任何特殊操作的情况下确保线程安全。
它通过使用线程锁来达成这一点；用一个锁来序列化对模块共享数据的访问，而
每个处理器也会创建一个锁来序列化对其下层 I/O 的访问。

如果你要使用 "signal" 模块来实现异步信号处理程序，则可能无法在这些处理
程序中使用 logging。 这是因为 "threading" 模块中的锁实现并非总是可重入
的，所以无法从此类信号处理程序唤起。


模块级函数
==========

在上述的类之外，还有一些模块级的函数。

logging.getLogger(name=None)

   返回一个由 name 指定名称的日志记录器，或者如果 name 为 "None" 则返
   回层级结构中的根日志记录器。 如果指定了 name，它通常是以点号分隔的
   带层级结构的名称如 *'a'*, *'a.b'* 或 *'a.b.c.d'*。 这些名称的选择完
   全取决于使用 logging 的开发者，不过就如在 记录器对象 中提到的那样建
   议使用 "__name__"，除非你有不这样做的特别理由。

   所有用给定的 name 对该函数的调用都将返回相同的日志记录器实例。 这意
   味着日志记录器实例不需要在应用的各部分间传递。

logging.getLoggerClass()

   返回标准的 "Logger" 类，或是最近传给 "setLoggerClass()" 的类。 此函
   数可以从一个新的类定义中调用，以确保安装自定义的 "Logger" 类不会撤
   销其他代码已经应用的自定义操作。 例如:

      class MyLogger(logging.getLoggerClass()):
          # ... 在这里重写行为

logging.getLogRecordFactory()

   返回一个被用来创建 "LogRecord" 的可调用对象。

   Added in version 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"。 所返回的映射是从每个对此函数的调用的内部映射
   拷贝的。

   Added in version 3.11.

logging.getLevelName(level)

   返回日志记录级别 *level* 的字符串表示。

   如果 *level* 为预定义的级别 "CRITICAL", "ERROR", "WARNING", "INFO"
   或 "DEBUG" 之一则你会得到相应的字符串。 如果你使用 "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"。

   Added in version 3.12.

logging.getHandlerNames()

   返回一个由所有已知处理器名称组成的不可变集合。

   Added in version 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*     | 使用指定的文件名创建一个 "FileHandler"，而不  |
   |                | 是 "StreamHandler"。                          |
   +----------------+-----------------------------------------------+
   | *filemode*     | 如果指定了 *filename*，则用此 模式 打开该文件 |
   |                | 。 默认模式为 "'a'"。                         |
   +----------------+-----------------------------------------------+
   | *format*       | 使用指定的格式字符串作为处理器。 默认为属性以 |
   |                | 冒号分隔的 "levelname", "name" 和 "message"。 |
   +----------------+-----------------------------------------------+
   | *datefmt*      | 使用指定的日期/时间格式，与 "time.strftime()" |
   |                | 所接受的格式相同。                            |
   +----------------+-----------------------------------------------+
   | *style*        | 如果指定了 *format*，将为格式字符串使用此风格 |
   |                | 。 "'%'", "'{'" 或 "'$'" 分别对应于 printf 风 |
   |                | 格, "str.format()" 或 "string.Template"。 默  |
   |                | 认为 "'%'"。                                  |
   +----------------+-----------------------------------------------+
   | *level*        | 设置根记录器级别为指定的 level.               |
   +----------------+-----------------------------------------------+
   | *stream*       | 使用指定的流初始化 "StreamHandler"。 请注意此 |
   |                | 参数与 *filename* 不兼 容 —— 如果两者同时存在 |
   |                | ，则会引发 "ValueError"。                     |
   +----------------+-----------------------------------------------+
   | *handlers*     | 如果指定，这应为一个包含要加入根日志记录器的  |
   |                | 已创建处理器的可迭代对 象。 任何尚未设置格式  |
   |                | 描述符的处理器将被设置为在此函数中创建的默认  |
   |                | 格 式描述符。 请注意此参数与 *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()

   通过刷新和关闭所有处理器来通知日志记录系统执行有序停止。 此函数应当
   在应用退出时被调用并且在此调用之后不应再使用日志记录系统。

   当 logging 模块被导入时，它会将此函数注册为退出处理程序 (参见
   "atexit")，因此通常不需要手动执行该操作。

logging.setLoggerClass(klass)

   通知日志记录系统在实例化日志记录器时使用 *klass* 类。 该类应当定义
   "__init__()" 使其只需要一个 name 参数，并且 "__init__()" 应当调用
   "Logger.__init__()"。 此函数通常会在需要使用自定义日志记录器行为的
   应用程序实例化任何日志记录器之前被调用。 在此调用之后，在其他任何时
   候都不要直接使用该子类来实例化日志记录器：请继续使用
   "logging.getLogger()" API 来获取你的日志记录器。

logging.setLogRecordFactory(factory)

   设置一个用来创建 "LogRecord" 的可调用对象。

   参数:
      **factory** -- 用来实例化日志记录的工厂可调用对象。

   Added in version 3.2: 此函数与 "getLogRecordFactory()" 一起提供，以
   便允许开发者对如何构造表示日志记录事件的 "LogRecord" 有更好的控制。

   可调用对象 factory 具有如下签名:

   "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

   通过此属性提供的“最后处理者”。 这是一个以 "WARNING" 级别写入到
   "sys.stderr" 的 "StreamHandler"，用于在没有任何日志记录配置的情况下
   处理日志记录事件。 最终结果就是将消息打印到 "sys.stderr"，这会替代
   先前形式为 "no handlers could be found for logger XYZ" 的错误消息。
   如果出于某种原因你需要先前的行为，可将 "lastResort" 设为 "None"。

   Added in version 3.2.

logging.raiseExceptions

   用于查看在处理过程中异常是否应当被传播。

   默认值: "True"。

   如果 "raiseExceptions" 为 "False"，则异常会被静默地忽略。 这大多数
   情况下是日志系统所需要的 —— 大多数用户不会关心日志系统中的错误，他
   们对应用程序错误更感兴趣。


与警告模块集成
==============

"captureWarnings()" 函数可被用于将 "logging" 与 "warnings" 模块进行集
成。

logging.captureWarnings(capture)

   此函数用于打开和关闭日志系统对警告的捕获。

   如果 *capture* 是 "True"，则 "warnings" 模块发出的警告将重定向到日
   志记录系统。具体来说，将使用 "warnings.formatwarning()" 格式化警告
   信息，并将结果字符串使用 "WARNING" 等级记录到名为 "'py.warnings'"
   的记录器中。

   如果 *capture* 是 "False"，则将停止将警告重定向到日志记录系统，并且
   将警告重定向到其原始目标（即在  "captureWarnings(True)"  调用之前的
   有效目标）。

参见:

  "logging.config" 模块
     日志记录模块的配置 API 。

  "logging.handlers" 模块
     日志记录模块附带的有用处理器。

  **PEP 282** - Logging 系统
     该提案描述了 Python 标准库中包含的这个特性。

  最初的 Python logging 包
     这是 "logging" 的原始来源。 该站点提供的软件包版本适用于 Python
     1.5.2, 2.1.x 和 2.2.x，它们未在标准库中包括 "logging" 包。
