logging.config ---日志配置

源代码: Lib/logging/config.py


本节介绍用于配置日志模块的API。

配置功能

以下功能配置日志记录模块。它们位于 logging.config 模块。它们的使用是可选的——您可以使用这些函数或通过调用主API(在中定义)来配置日志模块。 logging 以及定义在 logginglogging.handlers .

logging.config.dictConfig(config)

从字典获取日志配置。本词典的内容在 配置字典架构 下面。

如果在配置过程中遇到错误,此函数将引发 ValueErrorTypeErrorAttributeErrorImportError 带有适当的描述性信息。以下是将引发错误的条件(可能不完整)列表:

  • A level 它不是字符串,或者它不是与实际日志记录级别不对应的字符串。

  • A propagate 不是布尔值的值。

  • 没有相应目标的ID。

  • 在增量调用期间发现不存在的处理程序ID。

  • 记录器名称无效。

  • 无法解析为内部或外部对象。

解析由执行 DictConfigurator 类,其构造函数传递用于配置的字典,并具有 configure() 方法。这个 logging.config 模块具有可调用属性 dictConfigClass 初始设置为 DictConfigurator . 您可以替换 dictConfigClass 用你自己的一个合适的实现。

dictConfig() 调用 dictConfigClass 传递指定的字典,然后调用 configure() 返回的对象上的方法以使配置生效::

def dictConfig(config):
    dictConfigClass(config).configure()

例如,子类 DictConfigurator 可以调用 DictConfigurator.__init__() 在它自己的 __init__() ,然后设置自定义前缀,这些前缀将在后面的 configure() 调用。 dictConfigClass 将绑定到这个新的子类,然后 dictConfig() 可以完全按照默认的非自定义状态调用。

3.2 新版功能.

logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)

从中读取日志配置 configparser 格式化文件。文件格式应如中所述。 配置文件格式 . 可以从应用程序中多次调用此函数,从而允许最终用户从各种预屏蔽配置中进行选择(如果开发人员提供了一种呈现选择并加载所选配置的机制)。

参数
  • fname -- 文件名、类似文件的对象或派生自 RawConfigParser . 如果A RawConfigParser -已传递派生实例,它将按原样使用。否则,A Configparser 是实例化的,它从传入的对象中读取配置 fname . 如果有 readline() 方法,假定它是一个类似文件的对象,并使用 read_file() ;否则,假定它是一个文件名并传递给 read() .

  • defaults -- 可以在此参数中指定要传递给configparser的默认值。

  • disable_existing_loggers -- 如果指定为 False ,当进行此调用时存在的记录器保持启用状态。默认值为 True 因为这以向后兼容的方式启用了旧的行为。此行为用于禁用任何现有的非根记录器,除非它们或其祖先在日志配置中被显式命名。

在 3.4 版更改: 子类的实例 RawConfigParser 现在被接受为 fname . 这有助于:

  • 使用一个配置文件,其中日志配置只是整个应用程序配置的一部分。

  • 使用从文件中读取的配置,然后由使用应用程序修改(例如,基于命令行参数或运行时环境的其他方面),然后再传递给 fileConfig .

logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)

在指定端口上启动套接字服务器,并侦听新配置。如果没有指定端口,则模块的默认值为 DEFAULT_LOGGING_CONFIG_PORT 使用。日志配置将作为适合由处理的文件发送 dictConfig()fileConfig() . 返回A Thread 可以调用的实例 start() 启动服务器,您可以 join() 在适当的时候。要停止服务器,请致电 stopListening() .

这个 verify 参数(如果指定)应为可调用参数,该参数应验证通过套接字接收的字节是否有效并应进行处理。这可以通过对通过套接字发送的内容进行加密和/或签名来实现,这样 verify Callable可以执行签名验证和/或解密。这个 verify 使用单个参数调用Callable(可调用),即通过套接字接收的字节数,并应返回要处理的字节数,或者 None 以指示应丢弃字节。返回的字节可以与传入的字节相同(例如,仅在完成验证时),也可以完全不同(可能是在执行解密时)。

要将配置发送到套接字,请读取配置文件并将其作为一个字节序列发送到套接字,该字节序列前面是一个以二进制形式打包的四字节长度字符串,使用 struct.pack('>L', n) .

注解

因为部分配置是通过 eval() ,使用此功能可能会给用户带来安全风险。而函数只绑定到上的套接字 localhost ,因此不接受来自远程计算机的连接,在某些情况下,不受信任的代码可以在调用的进程的帐户下运行。 listen() . 具体来说,如果进程调用 listen() 在多用户计算机上运行,在多用户计算机上,用户不能相互信任,那么恶意用户只需连接到受害者的进程,就可以安排在受害者的进程中运行基本上任意的代码。 listen() 套接字并发送一个配置,该配置运行攻击者希望在受害者进程中执行的任何代码。这在使用默认端口时特别容易,但即使使用不同的端口也不难)。为了避免发生这种情况的风险,请使用 verify 参数 listen() 以防止应用未识别的配置。

在 3.4 版更改: 这个 verify 已添加参数。

注解

如果要向侦听器发送不禁用现有记录器的配置,则需要为配置使用JSON格式,该格式将使用 dictConfig() 用于配置。此方法允许您指定 disable_existing_loggers 作为 False 在您发送的配置中。

logging.config.stopListening()

停止通过调用创建的侦听服务器 listen() . 通常在调用之前调用 join() 返回值来自 listen() .

配置字典架构

描述日志配置需要列出要创建的各种对象以及它们之间的连接;例如,您可以创建一个名为“console”的处理程序,然后说名为“startup”的记录器将其消息发送到“console”处理程序。这些对象不限于 logging 模块,因为您可以编写自己的格式化程序或处理程序类。这些类的参数可能还需要包括外部对象,例如 sys.stderr . 描述这些对象和连接的语法在 对象连接 下面。

字典架构详细信息

字典传给 dictConfig() 必须包含以下键:

  • 版本 -设置为表示架构版本的整数值。目前唯一有效的值是1,但是拥有这个键允许模式在保持向后兼容性的同时发展。

所有其他键都是可选的,但如果存在,它们将按下面的说明进行解释。在下面提到“配置dict”的所有情况下,都将检查其是否具有 '()' 键查看是否需要自定义实例化。如果是这样,则 用户定义的对象 下面用于创建实例;否则,上下文用于确定要实例化的内容。

  • formatters -相应的值将是一个dict,其中每个键都是格式化程序ID,每个值都是一个dict,描述如何配置相应的 Formatter 实例。

    在配置字典中搜索与传递的参数相对应的以下可选键,以创建 Formatter 对象:

    • format

    • datefmt

    • style

    • validate (从版本>=3.8开始)

    可选的 class 键指示格式化程序类的名称(以虚线模块和类名表示)。实例化参数与 Formatter ,因此,此键对于实例化的自定义子类最有用 Formatter 。例如,Alternative类可能以扩展或压缩格式显示异常回溯。如果格式化程序需要不同的或额外的配置键,则应使用 用户定义的对象

  • 过滤器 -相应的值将是一个dict,其中每个键都是一个过滤器ID,每个值都是一个dict,描述如何配置相应的过滤器实例。

    在配置dict中搜索键 name (默认为空字符串),它用于构造 logging.Filter 实例。

  • 处理程序 -对应的值将是一个dict,其中每个键都是一个处理程序ID,每个值都是一个dict,描述如何配置对应的处理程序实例。

    在配置dict中搜索以下键:

    • class (强制性)。这是处理程序类的完全限定名。

    • level (可选)。处理程序的级别。

    • formatter (可选)。此处理程序的格式化程序的ID。

    • filters (可选)。此处理程序的筛选器ID列表。

    所有 other 键作为关键字参数传递给处理程序的构造函数。例如,给定片段:

    handlers:
      console:
        class : logging.StreamHandler
        formatter: brief
        level   : INFO
        filters: [allow_foo]
        stream  : ext://sys.stdout
      file:
        class : logging.handlers.RotatingFileHandler
        formatter: precise
        filename: logconfig.log
        maxBytes: 1024
        backupCount: 3
    

    ID为的处理程序 console 被实例化为 logging.StreamHandler 使用 sys.stdout 作为底层流。ID为的处理程序 file 被实例化为 logging.handlers.RotatingFileHandler 使用关键字参数 filename='logconfig.log', maxBytes=1024, backupCount=3 .

  • loggers -对应的值将是一个dict,其中每个键都是一个记录器名称,每个值都是一个dict,描述如何配置对应的记录器实例。

    在配置dict中搜索以下键:

    • level (可选)。记录器的级别。

    • propagate (可选)。记录器的传播设置。

    • filters (可选)。此记录器的筛选器ID列表。

    • handlers (可选)。此记录器处理程序的ID列表。

    将根据指定的级别、传播、筛选器和处理程序配置指定的记录器。

  • root -这将是根记录器的配置。配置的处理将与任何记录器相同,除了 propagate 设置将不适用。

  • 增量 -是否将配置解释为对现有配置的增量。此值默认为 False ,这意味着指定的配置使用与现有配置相同的语义替换现有配置。 fileConfig() 应用程序编程接口。

    如果指定值为 True ,配置将按照上一节中的说明进行处理。 增量配置 .

  • disable_existing_loggers -是否要禁用任何现有的非根记录器。此设置在中镜像相同名称的参数 fileConfig() . 如果不存在,则此参数默认为 True . 如果 增量True .

增量配置

很难为增量配置提供完全的灵活性。例如,由于过滤器和格式化程序等对象是匿名的,因此一旦设置了配置,就不可能在扩充配置时引用此类匿名对象。

此外,在运行时,一旦设置了一个配置,就没有一个令人信服的案例可以随意更改记录器、处理程序、过滤器、格式化程序的对象图;记录器和处理程序的冗长性可以通过设置级别来控制(对于记录器,还可以通过传播标志来控制)。在多线程环境中,以安全的方式随意更改对象图是有问题的;虽然这并非不可能,但其带来的好处并不值得在实现中增加复杂性。

因此,当 incremental 配置dict的键存在并且是 True ,系统将完全忽略 formattersfilters 并只处理 level 中的设置 handlers 条目,以及 levelpropagate 中的设置 loggersroot 条目。

在配置dict中使用一个值,可以将配置作为泡菜dict通过线路发送到套接字侦听器。因此,长时间运行的应用程序的日志记录冗长性可以随着时间而改变,而无需停止和重新启动应用程序。

对象连接

该模式描述了一组记录对象——记录器、处理程序、格式化程序、过滤器——它们在对象图中相互连接。因此,模式需要表示对象之间的连接。例如,假设配置后,特定的记录器将特定的处理程序附加到它上。为了讨论这个问题,我们可以说记录器代表两个连接的源,而处理程序代表两个连接的目的。当然,在配置的对象中,这由保存对处理程序的引用的记录器表示。在配置dict中,这是通过给每个目标对象一个明确标识它的ID来完成的,然后使用源对象配置中的ID来指示源对象和具有该ID的目标对象之间存在连接。

例如,考虑下面的yaml片段:

formatters:
  brief:
    # configuration for formatter with id 'brief' goes here
  precise:
    # configuration for formatter with id 'precise' goes here
handlers:
  h1: #This is an id
   # configuration of handler with id 'h1' goes here
   formatter: brief
  h2: #This is another id
   # configuration of handler with id 'h2' goes here
   formatter: precise
loggers:
  foo.bar.baz:
    # other configuration for logger 'foo.bar.baz'
    handlers: [h1, h2]

(注意:这里使用yaml是因为它的可读性比字典的等效python源格式稍高。)

记录器的ID是以编程方式用于获取对这些记录器的引用的记录器名称,例如。 foo.bar.baz . 格式化程序和筛选器的ID可以是任何字符串值(例如 briefprecise 上面)它们是暂时的,因为它们只对处理配置字典有意义,用于确定对象之间的连接,并且在配置调用完成时不会在任何地方持久化。

上面的代码段指示名为 foo.bar.baz 应该附加两个处理程序,由处理程序ID描述 h1h2 . 的格式化程序 h1 是用身份证描述的吗 brief 和格式化程序 h2 是用身份证描述的吗 precise .

用户定义的对象

该模式支持处理程序、筛选器和格式化程序的用户定义对象。(对于不同的实例,记录器不需要具有不同的类型,因此此配置模式不支持用户定义的记录器类。)

要配置的对象由详细描述其配置的字典描述。在某些地方,日志记录系统可以从上下文推断如何实例化对象,但是当用户定义的对象要被实例化时,系统将不知道如何这样做。为了为用户定义的对象实例化提供完全的灵活性,用户需要提供一个“工厂”——一个可调用的,用配置字典调用,并返回实例化的对象。这是通过在专用钥匙下提供的工厂的绝对输入路径发出的信号。 '()' . 下面是一个具体的例子:

formatters:
  brief:
    format: '%(message)s'
  default:
    format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
    datefmt: '%Y-%m-%d %H:%M:%S'
  custom:
      (): my.package.customFormatterFactory
      bar: baz
      spam: 99.9
      answer: 42

上面的yaml片段定义了三个格式化程序。第一个,带身份证 brief 是一个标准 logging.Formatter 具有指定格式字符串的实例。第二个,带身份证 default ,具有更长的格式并显式定义时间格式,并且将导致 logging.Formatter 用这两个格式字符串初始化。以python源代码形式显示, briefdefault 格式化程序具有配置子字典::

{
  'format' : '%(message)s'
}

和:

{
  'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
  'datefmt' : '%Y-%m-%d %H:%M:%S'
}

因为这些字典不包含特殊键 '()' ,根据上下文推断实例化:结果,标准 logging.Formatter 创建实例。第三个格式化程序的配置子字典,ID为 custom 是:

{
  '()' : 'my.package.customFormatterFactory',
  'bar' : 'baz',
  'spam' : 99.9,
  'answer' : 42
}

这里面有一把特殊的钥匙 '()' 这意味着需要用户定义的实例化。在这种情况下,将使用指定的工厂可调用文件。如果它是一个实际的可调用文件,那么它将被直接使用——否则,如果您指定一个字符串(如示例中所示),那么实际的可调用文件将使用普通的导入机制来定位。将使用 剩下的 配置子字典中的项作为关键字参数。在上面的示例中,ID为的格式化程序 custom 将假定由调用返回:

my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)

钥匙 '()' 已用作特殊键,因为它不是有效的关键字参数名称,因此不会与调用中使用的关键字参数的名称冲突。这个 '()' 也可以用作相应值是可调用的助记符。

访问外部对象

例如,有时配置需要引用配置外部的对象 sys.stderr . 如果配置dict是使用python代码构建的,那么这很简单,但是当通过文本文件(例如json、yaml)提供配置时会出现问题。在文本文件中,没有标准的方法来区分 sys.stderr 从文本字符串 'sys.stderr' . 为了便于区分,配置系统在字符串值中查找特定的特殊前缀,并对它们进行特殊处理。例如,如果文本字符串 'ext://sys.stderr' 在配置中作为值提供,然后 ext:// 将被剥离,剩余的值将使用正常的导入机制进行处理。

这种前缀的处理方式类似于协议处理:有一种通用机制来查找与正则表达式匹配的前缀。 ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ 据此,如果 prefix 是公认的, suffix 以前缀相关的方式处理,处理结果将替换字符串值。如果不识别前缀,则字符串值将保持原样。

访问内部对象

除了外部对象之外,有时还需要在配置中引用对象。这将由配置系统隐式地为它所知道的事情完成。例如,字符串值 'DEBUG' 对于一个 level 在记录器或处理程序中,将自动转换为值 logging.DEBUGhandlersfiltersformatter 条目将获取对象ID并解析为适当的目标对象。

但是,对于用户定义的对象(这些对象不为 logging 模块。例如,考虑 logging.handlers.MemoryHandler ,需要一个 target 参数,它是要委托给的另一个处理程序。由于系统已经知道这个类,那么在配置中,给定的 target 只需要相关目标处理程序的对象ID,系统将从该ID解析为处理程序。但是,如果用户定义了 my.package.MyHandler 它有一个 alternate 处理程序,配置系统将不知道 alternate 引用了一个处理程序。为了解决这一问题,通用的解决方案系统允许用户指定:

handlers:
  file:
    # configuration of file handler goes here

  custom:
    (): my.package.MyHandler
    alternate: cfg://handlers.file

文本字符串 'cfg://handlers.file' 将以类似的方式解决 ext:// 前缀,但查找配置本身而不是导入命名空间。该机制允许通过点或索引访问,方式与 str.format . 因此,给出以下片段:

handlers:
  email:
    class: logging.handlers.SMTPHandler
    mailhost: localhost
    fromaddr: my_app@domain.tld
    toaddrs:
      - support_team@domain.tld
      - dev_team@domain.tld
    subject: Houston, we have a problem.

在配置中,字符串 'cfg://handlers' 会用键决定听写 handlers 字符串 'cfg://handlers.email 会用键决定听写 emailhandlers 听写等等。弦 'cfg://handlers.email.toaddrs[1] 会下决心 'dev_team.domain.tld''cfg://handlers.email.toaddrs[0]' 将解析为值 'support_team@domain.tld' . 这个 subject 可以使用以下方法访问值: 'cfg://handlers.email.subject' 或者,等价地, 'cfg://handlers.email[subject]' . 只有当键包含空格或非字母数字字符时,才需要使用后一种形式。如果索引值仅包含十进制数字,则将尝试使用相应的整数值进行访问,如果需要,将返回到字符串值。

给定字符串 cfg://handlers.myhandler.mykey.123 ,这将决定 config_dict['handlers']['myhandler']['mykey']['123'] . 如果字符串指定为 cfg://handlers.myhandler.mykey[123] ,系统将尝试从 config_dict['handlers']['myhandler']['mykey'][123] 然后回到 config_dict['handlers']['myhandler']['mykey']['123'] 如果失败了。

导入解决方案和自定义导入程序

默认情况下,导入分辨率使用内置 __import__() 函数进行导入。您可能希望用您自己的导入机制替换它:如果是这样,您可以替换 importer 的属性 DictConfigurator 或者它的超类 BaseConfigurator 类。但是,您需要小心,因为函数是通过描述符从类中访问的。如果使用可调用的python进行导入,并且希望在类级别而不是实例级别定义它,则需要用 staticmethod() . 例如::

from importlib import import_module
from logging.config import BaseConfigurator

BaseConfigurator.importer = staticmethod(import_module)

你不需要封装 staticmethod() 如果要在配置器上设置可调用的导入 实例 .

配置文件格式

配置文件格式 fileConfig() 基于 configparser 功能。文件必须包含名为 [loggers][handlers][formatters] 它按名称标识文件中定义的每种类型的实体。对于每个这样的实体,都有一个单独的部分来标识该实体的配置方式。因此,对于一个名为 log01[loggers] 节,相关配置详细信息保存在节中。 [logger_log01] . 类似地,一个处理程序调用 hand01[handlers] 节的配置将保存在名为 [handler_hand01] ,而格式化程序调用 form01[formatters] 节的配置将在名为 [formatter_form01] . 必须在名为 [logger_root] .

注解

这个 fileConfig() API比 dictConfig() 并且不提供涵盖日志记录某些方面的功能。例如,您不能配置 Filter 对象,用于过滤简单整数级以外的消息,使用 fileConfig() . 如果您需要 Filter 在日志配置中,需要使用 dictConfig() . 请注意,将来对配置功能的增强将添加到 dictConfig() 因此,在方便的时候考虑转换到这个新的API是值得的。

下面给出了文件中这些部分的示例。

[loggers]
keys=root,log02,log03,log04,log05,log06,log07

[handlers]
keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09

[formatters]
keys=form01,form02,form03,form04,form05,form06,form07,form08,form09

根记录器必须指定处理程序的级别和列表。下面给出了一个根记录器部分的示例。

[logger_root]
level=NOTSET
handlers=hand01

这个 level 条目可以是 DEBUG, INFO, WARNING, ERROR, CRITICALNOTSET . 仅对于根记录器, NOTSET 表示将记录所有消息。级别值为 eval()logging 包的命名空间。

这个 handlers 条目是处理程序名称的逗号分隔列表,必须出现在 [handlers] 部分。这些名称必须出现在 [handlers] 并在配置文件中具有相应的节。

对于根记录器以外的记录器,需要一些附加信息。下面的例子说明了这一点。

[logger_parser]
level=DEBUG
handlers=hand01
propagate=1
qualname=compiler.parser

这个 levelhandlers 条目被解释为根记录器,除非非根记录器的级别被指定为 NOTSET ,系统向更高层次的记录器查询,以确定记录器的有效级别。这个 propagate 条目设置为1表示消息必须传播到此记录器的更高层次结构上的处理程序,或0表示消息 not 传播到层次结构上的处理程序。这个 qualname 条目是记录器的层次通道名称,也就是说应用程序用于获取记录器的名称。

下面举例说明了指定处理程序配置的部分。

[handler_hand01]
class=StreamHandler
level=NOTSET
formatter=form01
args=(sys.stdout,)

这个 class 条目指示处理程序的类(由 eval()logging 包的命名空间)。这个 level 解释为记录员,以及 NOTSET 意思是“记录一切”。

这个 formatter 条目指示此处理程序的格式化程序的键名。如果为空,则为默认格式设置工具 (logging._defaultFormatter )。如果指定了名称,它必须出现在 [formatters] 并在配置文件中具有相应的节。

这个 args 进入时 eval()logging 包的命名空间是处理程序类的构造函数的参数列表。请参阅相关处理程序的构造函数或下面的示例,以了解如何构造典型条目。如果未提供,则默认为 () .

可选的 kwargs 进入时 eval()logging 包的命名空间是处理程序类的构造函数的关键字参数dict。如果未提供,则默认为 {{}} .

[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form02
args=('python.log', 'w')

[handler_hand03]
class=handlers.SocketHandler
level=INFO
formatter=form03
args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)

[handler_hand04]
class=handlers.DatagramHandler
level=WARN
formatter=form04
args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)

[handler_hand05]
class=handlers.SysLogHandler
level=ERROR
formatter=form05
args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)

[handler_hand06]
class=handlers.NTEventLogHandler
level=CRITICAL
formatter=form06
args=('Python Application', '', 'Application')

[handler_hand07]
class=handlers.SMTPHandler
level=WARN
formatter=form07
args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
kwargs={'timeout': 10.0}

[handler_hand08]
class=handlers.MemoryHandler
level=NOTSET
formatter=form08
target=
args=(10, ERROR)

[handler_hand09]
class=handlers.HTTPHandler
level=NOTSET
formatter=form09
args=('localhost:9022', '/log', 'GET')
kwargs={'secure': True}

指定格式化程序配置的部分由以下内容组成。

[formatter_form01]
format=F1 %(asctime)s %(levelname)s %(message)s
datefmt=
style='%'
validate=True
class=logging.Formatter

格式化程序配置的参数与字典架构中的键相同 formatters section

注解

由于使用 eval() 如上所述,使用 listen() 通过套接字发送和接收配置。风险仅限于没有相互信任的多个用户在同一台计算机上运行代码;请参见 listen() 有关详细信息的文档。

参见

模块 logging

日志模块的API引用。

模块 logging.handlers

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