Skip to main content

16.7. logging.config —记录配置

源代码: Lib/logging/config.py


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

16.7.1. 配置功能

以下功能配置记录模块。它们位于 logging.config 模块中。它们的使用是可选的 - 您可以使用这些函数或通过调用主API(在 logging 自身中定义)和定义在 logginglogging.handlers 中声明的处理程序来配置日志记录模块。

logging.config.dictConfig(config)

从字典中获取日志配置。这个字典的内容在下面的 配置字典模式 中描述。

如果在配置期间遇到错误,则该函数将产生具有适当描述性消息的 ValueErrorTypeErrorAttributeErrorImportError。以下是一个(可能不完整)的条件列表,它会引发错误:

  • level 不是字符串或是不对应于实际日志记录级别的字符串。

  • propagate 值不是布尔值。

  • 一个没有相应目的地的id。

  • 在增量调用期间找到的不存在的处理程序标识。

  • 无效的记录器名称。

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

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

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

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

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

3.2 新版功能.

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

configparser -format文件读取日志记录配置。文件的格式应该如 配置文件格式 中所述。该功能可以从应用程序中调用多次,允许最终用户从各种预先配置的配置中进行选择(如果开发人员提供了一种机制来呈现选择并加载所选择的配置)。

参数:
  • fname – 文件名或类文件对象,或从 RawConfigParser 派生的实例。如果通过 RawConfigParser 派生的实例,则按原样使用。否则,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() 处理的文件发送。返回一个 Thread 实例,您可以在该实例上调用 start() 来启动服务器,并在适当时可以使用 join()。要停止服务器,请调用 stopListening()

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

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

注解

由于配置的部分通过 eval(),使用此功能可能会打开其用户的安全风险。虽然该函数仅绑定到 localhost 上的套接字,并且不接受来自远程机器的连接,但是存在不可信代码可以在调用 listen() 的进程的帐户下运行的情况。特别地,如果调用 listen() 的进程在多用户机器上运行,其中用户不能彼此信任,则恶意用户可以安排在受害用户的进程中运行基本上任意的代码,只需通过连接到受害者的 listen() 套接字并发送配置它运行攻击者想要在受害者进程中执行的任何代码。这是特别容易做,如果使用默认端口,但不是硬,即使使用不同的端口)。为了避免发生这种情况,请使用 listen()verify 参数以防止应用无法识别的配置。

在 3.4 版更改: 添加了 verify 参数。

注解

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

logging.config.stopListening()

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

16.7.2. 配置字典模式

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

16.7.2.1. 字典模式详细信息

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

  • version - 设置为表示模式版本的整数值。目前唯一有效的值为1,但具有此密钥允许模式演进,同时仍保留向后兼容性。

所有其他键都是可选的,但如果存在,它们将被解释如下所述。在下面提到的配置dict的所有情况下,将检查特殊 '()' 密钥以查看是否需要定制实例化。如果是,则使用下面 用户定义的对象 中描述的机制来创建实例;否则,上下文用于确定要实例化的内容。

  • formatters - 相应的值将是一个dict,其中每个键是一个格式化程序标识符,每个值是一个说明如何配置相应的 Formatter 实例的字典。

    对配置的dict搜索密钥 formatdatefmt (具有 None 的默认值),并且这些用于构造 Formatter 实例。

  • filters - 相应的值将是一个dict,其中每个键是一个过滤器id,每个值是一个说明如何配置相应的Filter实例的dict。

    配置dict搜索密钥 name (默认为空字符串),并且这用于构造 logging.Filter 实例。

  • handlers - 相应的值将是一个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 的处理程序被实例化为具有关键字参数 filename='logconfig.log', maxBytes=1024, backupCount=3logging.handlers.RotatingFileHandler

  • loggers - 相应的值将是一个dict,其中每个键是一个记录器名称,每个值是一个说明如何配置相应Logger实例的dict。

    将为配置的dict搜索以下键:

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

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

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

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

    将根据指定的级别,传播,过滤器和处理程序配置指定的记录器。

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

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

    如果指定的值是 True,则按照 增量配置 部分中所述处理配置。

  • disable_existing_loggers - 是否禁用任何现有的记录器。此设置反映 fileConfig() 中同名的参数。如果不存在,则此参数默认为 True。如果 incrementalTrue,则忽略此值。

16.7.2.2. 增量配置

很难为增量配置提供完全的灵活性。例如,因为诸如过滤器和格式化器之类的对象是匿名的,所以一旦配置被设置,在扩充配置时就不可能引用这样的匿名对象。

此外,一旦配置被建立,在运行时任意地改变记录器,处理器,过滤器,格式化器的对象图就不是有说服力的情况;可以通过设置级别(以及在记录器的情况下,传播标志)来控制记录器和处理器的冗长性。以安全的方式任意地改变对象图在多线程环境中是有问题的;虽然不是不可能,但是它的好处并不值得它为实现增加的复杂性。

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

在配置中使用值dict会使配置通过线路发送,如pickled dicts到套接字侦听器。因此,长时间运行的应用程序的日志记录冗长度可以随时间改变,而无需停止和重新启动应用程序。

16.7.2.3. 对象连接

该模式描述了一组日志对象 - 日志记录器,处理程序,格式化程序,过滤器 - 它们在对象图中彼此连接。因此,模式需要表示对象之间的连接。例如,说,一旦配置,特定的记录器已经附加到它特定的处理程序。为了本讨论的目的,我们可以说记录器表示两者之间的连接的源,而处理器是目的地。当然,在配置的对象中,这由记录器保存对处理程序的引用来表示。在配置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。格式化器和过滤器的ids可以是任何字符串值(如上面的 briefprecise),并且它们是瞬态的,因为它们只对处理配置字典有意义,并且用于确定对象之间的连接,并且在配置调用完成。

上面的代码段表示,名为 foo.bar.baz 的日志记录器应该连接两个处理程序,由处理程序ids h1h2 描述。 h1 的格式化器由id brief 描述,并且 h2 的格式化器由id precise 描述。

16.7.2.4. 用户定义的对象

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

要配置的对象由详细描述其配置的字典描述。在一些地方,日志记录系统将能够从上下文中推断对象如何被实例化,但是当用户定义的对象被实例化时,系统将不知道如何做到这一点。为了为用户定义的对象实例化提供完全的灵活性,用户需要提供一个“factory” - 一个可调用,它用配置字典调用并返回实例化的对象。这通过在特殊键 '()' 下可用的工厂的绝对导入路径来表示。这里有一个具体的例子:

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代码段定义了三个格式化程序。第一个,具有ID brief,是具有指定格式字符串的标准 logging.Formatter 实例。第二个,具有id 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)

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

16.7.2.5. 访问外部对象

有时,配置需要引用配置外部的对象,例如 sys.stderr。如果配置dict是使用Python代码构建的,这是直接的,但是当通过文本文件(例如JSON,YAML)提供配置时出现问题。在文本文件中,没有标准方法来区分 sys.stderr 和文字字符串 'sys.stderr'。为了方便这种区分,配置系统在字符串值中查找某些特殊的前缀并对其进行特殊处理。例如,如果文本字符串 'ext://sys.stderr' 作为配置中的值提供,则 ext:// 将被剥离,并且该值的其余部分使用正常的导入机制来处理。

以类似于协议处理的方式来进行对这样的前缀的处理:存在寻找与正则表达式 ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ 匹配的前缀的通用机制,由此,如果识别出 prefix,则以前缀依赖的方式处理 suffix,并且处理的结果替换字符串值。如果前缀不被识别,则字符串值将保持原样。

16.7.2.6. 访问内部对象

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

然而,对于 logging 模块不知道的用户定义对象需要更通用的机制。例如,考虑 logging.handlers.MemoryHandler,它接受一个 target 参数,它是另一个要委托的处理程序。由于系统已经知道这个类,所以在配置中,给定的 target 只需要是相关目标处理程序的对象id,并且系统将从id解析为处理程序。然而,如果用户定义具有 alternate 处理程序的 my.package.MyHandler,则配置系统将不知道 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 的dict,字符串 'cfg://handlers.email 将解析为具有 handlers dict中的密钥 email 的dict,等等。字符串 'cfg://handlers.email.toaddrs[1] 将解析为 'dev_team.domain.tld',字符串 'cfg://handlers.email.toaddrs[0]' 将解析为值 'support_team@domain.tld'。可以使用 'cfg://handlers.email.subject' 或等效地使用 'cfg://handlers.email[subject]' 来访问 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']

16.7.2.7. 导入分辨率和自定义导入器

默认情况下,导入分辨率使用内置的 __import__() 函数进行导入。您可能想用您自己的导入机制替换它:如果是,您可以替换 DictConfigurator 或其超类 BaseConfigurator 类的 importer 属性。但是,由于通过描述符从类访问函数的方式,您需要小心。如果你使用一个Python调用来做你的导入,并且你想要在类级别而不是实例级别定义它,你需要用 staticmethod() 包装它。例如:

from importlib import import_module
from logging.config import BaseConfigurator

BaseConfigurator.importer = staticmethod(import_module)

如果您在配置器 instance 上设置import callable,则不需要使用 staticmethod() 换行。

16.7.3. 配置文件格式

fileConfig() 理解的配置文件格式基于 configparser 功能。该文件必须包含名为 [loggers][handlers][formatters] 的段,这些段通过名称标识文件中定义的每个类型的实体。对于每个这样的实体,存在标识如何配置该实体的单独部分。因此,对于在 [loggers] 部分中名为 log01 的记录器,相关的配置细节保存在 [logger_log01] 部分中。类似地,在 [handlers] 部分中称为 hand01 的处理器将其配置保持在称为 [handler_hand01] 的部分中,而在 [formatters] 部分中称为 form01 的格式化器将在称为 [formatter_form01] 的部分中指定其配置。根记录器配置必须在称为 [logger_root] 的节中指定。

注解

fileConfig() API比 dictConfig() API更旧,并且不提供涵盖日志记录的某些方面的功能。例如,您不能配置 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 意味着将记录所有消息。在 logging 包名称空间的上下文中,级别值为 eval()

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

对于除根记录器以外的记录器,需要一些附加信息。这通过以下示例来说明。

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

levelhandlers 条目被解释为根记录器,除非如果非根记录器的级别被指定为 NOTSET,则系统参考层次结构上方的记录器以确定记录器的有效级别。 propagate 条目设置为1,表示消息必须传播到记录器层次结构中从该记录器向上较高的处理程序,或0,以指示消息是 传播到层次结构中的处理程序。 qualname 条目是记录器的分层通道名称,也就是说应用程序用来获取记录器的名称。

指定处理程序配置的部分通过以下示例。

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

class 条目指示处理程序的类(由 logging 包命名空间中的 eval() 确定)。 level 由日志器解释,NOTSET 被认为是“记录一切”。

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

当在 logging 包命名空间的上下文中 eval() 时,args 条目是处理程序类的构造函数的参数列表。参考相关处理程序的构造函数,或下面的例子,看看如何构造典型的条目。

[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')

[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')

指定格式化程序配置的部分由以下代表。

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

format 条目是总体格式字符串,datefmt 条目是 strftime() -compatible日期/时间格式字符串。如果为空,包将替换ISO8601格式的日期/时间,这几乎等同于指定日期格式字符串 '%Y-%m-%d %H:%M:%S'。 ISO8601格式还指定了毫秒,它附加到使用上述格式字符串的结果,并带有逗号分隔符。 ISO8601格式的示例时间是 2003-01-23 00:29:50,411

class 条目是可选的。它指示格式化程序类的名称(作为虚线模块和类名称。)此选项对于实例化 Formatter 子类很有用。 Formatter 的子类可以以扩展或压缩格式提供异常跟踪。

注解

由于使用如上所述的 eval(),存在由使用 listen() 经由套接字发送和接收配置而导致的潜在安全风险。风险仅限于没有相互信任的多个用户在同一台机器上运行代码的情况;有关详细信息,请参阅 listen() 文档。

参见

模块 logging

日志模块的API参考。

模块 logging.handlers

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