15.8. logging.config — 日志配置 — Python 文档

来自菜鸟教程
Python/docs/2.7/library/logging.config
跳转至:导航、​搜索

15.8. 日志配置文件 — 日志配置

重要的

此页面仅包含参考信息。 有关教程,请参阅

源代码: :source:`Lib/logging/config.py`



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

15.8.1. 配置功能

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

logging.config.dictConfig(config)

从字典中获取日志记录配置。 这个字典的内容在下面的配置字典架构中描述。

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

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

  • propagate 不是布尔值的值。

  • 没有相应目的地的 id。

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

  • 无效的记录器名称。

  • 无法解决内部或外部对象。

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

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

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

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

2.7 版中的新功能。

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

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

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

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

2.6 版更改: 添加了 disable_existing_loggers 关键字参数。 以前,现有的记录器 总是 禁用。

logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT)

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

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

笔记

由于部分配置通过 eval() 传递,因此使用此函数可能会使用户面临安全风险。 虽然该函数仅绑定到 localhost 上的套接字,因此不接受来自远程机器的连接,但在某些情况下,可能会在调用 listen()[ 的进程的帐户下运行不受信任的代码X224X]。 具体来说,如果调用 listen() 的进程在用户无法相互信任的多用户机器上运行,那么恶意用户可以安排在受害者用户的进程中运行基本上任意代码,只需连接到受害者的 listen() 套接字并发送一个配置,该配置运行攻击者想要在受害者进程中执行的任何代码。 如果使用默认端口,这特别容易做到,但即使使用不同的端口也不难)。

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


15.8.2. 配置字典架构

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

15.8.2.1. 字典架构详细信息

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

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

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

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

    在配置字典中搜索键 formatdatefmt(默认为 None),这些用于构建 Formatter 实例。

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

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

  • handlers - 对应的值将是一个 dict,其中每个键是一个处理程序 ID,每个值是一个描述如何配置相应 Handler 实例的 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 - 对应的值将是一个字典,其中每个键是一个记录器名称,每个值是一个描述如何配置相应 Logger 实例的字典。

    在配置字典中搜索以下键:

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

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

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

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

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

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

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

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

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


15.8.2.2. 增量配置

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

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

因此,当配置字典的 incremental 键存在且为 True 时,系统将完全忽略任何 formattersfilters 条目,并且只处理handlers 条目中的 level 设置,以及 loggersroot 条目中的 levelpropagate 设置.

在配置字典中使用一个值可以让配置作为腌制字典通过网络发送到套接字侦听器。 因此,长时间运行的应用程序的日志记录详细程度可以随着时间的推移而改变,而无需停止和重新启动应用程序。


15.8.2.3. 对象连接

该模式描述了一组日志对象——记录器、处理程序、格式化程序、过滤器——它们在对象图中相互连接。 因此,模式需要表示对象之间的连接。 例如,假设一旦配置,一个特定的记录器就附加了一个特定的处理程序。 出于本次讨论的目的,我们可以说记录器代表两者之间的连接的源,而处理程序代表目标。 当然,在配置的对象中,这由持有对处理程序的引用的记录器表示。 在配置字典中,这是通过给每个目标对象一个明确标识它的 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。 Formatters 和 Filters 的 id 可以是任何字符串值(例如上面的 briefprecise)并且它们是暂时的,因为它们仅对处理配置字典有意义并用于确定连接对象之间,并且在配置调用完成后不会在任何地方持久化。

上面的代码片段表明名为 foo.bar.baz 的记录器应该附加两个处理程序,它们由处理程序 ID h1h2 描述。 h1的格式化程序是由id brief描述的,h2的格式化程序是由id precise描述的。


15.8.2.4. 用户定义对象

该架构支持用于处理程序、过滤器和格式化程序的用户定义对象。 (对于不同的实例,记录器不需要有不同的类型,所以这个配置模式不支持用户定义的记录器类。)

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

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
}

并且它包含特殊键 '()',这意味着需要用户定义的实例化。 在这种情况下,将使用指定的工厂调用。 如果它是一个实际的可调用对象,它将被直接使用 - 否则,如果您指定一个字符串(如示例中所示),则将使用正常的导入机制来定位实际的可调用对象。 callable 将使用配置子字典中的 剩余 项作为关键字参数来调用。 在上面的例子中,id 为 custom 的格式化程序将被假定为由调用返回:

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

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


15.8.2.5. 访问外部对象

有时配置需要引用配置外部的对象,例如 sys.stderr。 如果配置字典是使用 Python 代码构建的,这很简单,但是当配置通过文本文件(例如 JSON、YAML)。 在文本文件中,没有标准方法可以将 sys.stderr 与文字字符串 'sys.stderr' 区分开来。 为了便于区分,配置系统会在字符串值中查找某些特殊前缀并对其进行特殊处理。 例如,如果文本字符串 'ext://sys.stderr' 在配置中作为一个值提供,那么 ext:// 将被剥离,其余的值将使用正常的导入机制进行处理。

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


15.8.2.6. 访问内部对象

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

但是,对于 logging 模块不知道的用户定义对象,需要更通用的机制。 例如,考虑 logging.handlers.MemoryHandler,它接受一个 target 参数,这是另一个要委托给的处理程序。 既然系统已经知道这个类,那么在配置中,给定的target只要是相关目标handler的object id,系统就会根据id解析到handler。 但是,如果用户定义了具有 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 的字典,字符串 'cfg://handlers.email 将解析为 email 键的字典X162X] dict,依此类推。 字符串 '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']


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

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

from importlib import import_module
from logging.config import BaseConfigurator

BaseConfigurator.importer = staticmethod(import_module)

如果您在配置器 实例 上设置可调用的导入,则不需要用 staticmethod() 包装。


15.8.3. 配置文件格式

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

笔记

fileConfig() API 比 dictConfig() API 更旧,并且不提供涵盖日志记录某些方面的功能。 例如,您不能使用 fileConfig() 配置 Filter 对象,这些对象用于过滤简单整数级别之外的消息。 如果您需要在日志配置中包含 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 被认为意味着“记录一切”。

在 2.6 版中更改: 添加了对将处理程序的类解析为带点的模块和类名的支持。


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

args 条目,当 eval()logging 包的命名空间的上下文中使用时,是处理程序类的构造函数的参数列表。 请参阅相关处理程序的构造函数或以下示例,了解典型条目的构造方式。

[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() 兼容的日期/时间格式字符串。 如果为空,则包替换 ISO8601 格式日期/时间,这几乎等同于指定日期格式字符串 '%Y-%m-%d %H:%M:%S'。 ISO8601 格式还指定了毫秒,这些毫秒会附加到使用上述格式字符串的结果中,并带有逗号分隔符。 ISO8601 格式的示例时间是 2003-01-23 00:29:50,411

class 条目是可选的。 它指示格式化程序类的名称(作为带点的模块和类名。)此选项对于实例化 Formatter 子类很有用。 Formatter 的子类可以以扩展或压缩格式呈现异常回溯。

笔记

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


也可以看看

模块记录
日志记录模块的 API 参考。
模块 logging.handlers
日志模块中包含的有用处理程序。