16.7. logging.config - 记录配置

源代码: Lib / logging / config.py

该章描述用于配置日志模块的API。

16.7.1. Configuration functions

下列函数配置日志模块。它们都在logging.config模块中。这些使用是可选的—你可以使用这些函数配置日志模块或者通过调用定义在logging模块中的主要API来配置,或者通过定义logginglogging.handlers中的handlers来配置日志模块。

logging.config.dictConfig(config)

Takes the logging configuration from a dictionary. The contents of this dictionary are described in Configuration dictionary schema below.

If an error is encountered during configuration, this function will raise a ValueError, TypeError, AttributeError or ImportError with a suitably descriptive message. The following is a (possibly incomplete) list of conditions which will raise an error:

  • A level which is not a string or which is a string not corresponding to an actual logging level.
  • A propagate value which is not a boolean.
  • An id which does not have a corresponding destination.
  • A non-existent handler id found during an incremental call.
  • An invalid logger name.
  • Inability to resolve to an internal or external object.

Parsing is performed by the DictConfigurator class, whose constructor is passed the dictionary used for configuration, and has a configure() method. The logging.config module has a callable attribute dictConfigClass which is initially set to DictConfigurator. You can replace the value of dictConfigClass with a suitable implementation of your own.

dictConfig() calls dictConfigClass passing the specified dictionary, and then calls the configure() method on the returned object to put the configuration into effect:

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

For example, a subclass of DictConfigurator could call DictConfigurator.__init__() in its own __init__(), then set up custom prefixes which would be usable in the subsequent configure() call. dictConfigClass would be bound to this new subclass, and then dictConfig() could be called exactly as in the default, uncustomized state.

版本3.2中的新功能。

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

configparser -format文件读取日志记录配置。文件格式描述于Configuration file format该函数可以从应用中多次调用,允许最终用户可以从各种预先配置好的配置中选择(如果开发者提供了展示配置并装载选择的配置的机制的话)。

参数:
  • fname - 文件名或类文件对象,或从RawConfigParser派生的实例。If a RawConfigParser-derived instance is passed, it is used as is. 否则,将实例化Configparser,并且从fname中传递的对象读取配置。如果有一个readline()方法,则假定它是一个类文件对象,并使用read_file()读取;否则,假定它是一个文件名,并传递给read()
  • defaults – 该参数指明了传给ConfigParser的默认值。
  • disable_existing_loggers - 如果指定为False,则在进行此调用时存在的记录器将保持启用状态。默认值为True以保证老的行为向前兼容。该行为禁用已存的logger,除非它们或者它们的祖先明确的出现在日志配置中。

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

  • 使用配置文件,其中日志配置只是整个应用程序配置的一部分。
  • 使用从文件读取的配置,然后由使用应用程序修改(例如,基于命令行参数或运行时环境的其他方面),然后传递给fileConfig
logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)

在指定的端口起一个socket服务器,以监听新的配置。如果没有指定端口,使用模块默认的DEFAULT_LOGGING_CONFIG_PORT日志配置以文件形式发送,可以用fileConfig()处理。返回Thread实例,可以在该线程上调用start()来启动服务器,可以在合适的时候join()该线程。调用stopListening()以停止服务器。

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

要向socket发送配置,从配置文件中读入配置,将其以字节流的方式发送至socket,最开始为4字节长的字节流的二进制长度,即struct.pack('>L', n)

注意

因为部分配置会传给eval(),使用该函数可能有安全风险。当该函数只绑定于localhost的socket,也不接受远端机器的连接,调用listen()的进程的帐号就有可能运行不受信任的代码。特别的,如果调用listen()的进程运行于多用户机器,而这些用户彼此并不信任,那么一个恶意用户可以在受害用户的进程中运行本质上来说任意的代码,只需要连接到受害用户的listen() socket,然后发送配置,其实是攻击者想要在受害者进程中执行的任何代码。如果使用默认端口这尤为容易,就算是用了不同的端口也不算太难。为避免发生这种情况,请使用listen()verify参数防止应用无法识别的配置。

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

logging.config.stopListening()

停止listen()返回的服务器的监听。典型的在listen()的返回值上调用join()之前调用该函数。

16.7.2. Configuration dictionary schema

描述日志记录配置需要列出要创建的各种对象以及它们之间的连接;例如,您可以创建一个名为“console”的处理程序,然后说,名为“startup”的记录器将其消息发送到“控制台”处理程序。并不限于logging模块所提供的对象,因为你可以写自己的formatter或者handler类。 这些类的参数也许需要包含sys.stderr这样的外部对象。描述对象将其连接的语法定义于下面的Object connections

16.7.2.1. Dictionary Schema Details

传递给dictConfig()的字典必需包含下列键值:

  • version - 表示模式版本的整数值。目前唯一的有效值为1,但是有这个键允许模式演化,同时保留了向前兼容性。

所有其它键都是可选的,但是如果存在,如下述解读。在下面如果提到了‘配置字典’,特殊键'()'将会被检查,以确定是否需要自定义的初始化。如果是,则使用以下User-defined objects中描述的机制创建实例;否则,上下文用于确定要实例化的内容。

  • formatters - 对应的值是个字典,其每一个键都是formatter标识符,每一个值又是个字典,描述了如何配置对应的 Formatter 实例。

    搜寻配置字典中的formatdatefmt键,(默认为None),以构造Formatter实例。

  • filters - 对应的值是个字典,其每一个键都是filter标识符,每一个值又是个字典,描述了如何配置对应的 Filter 实例。

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

  • handlers - 对应的值是个字典,其每一个键都是handler标识符,每一个值又是个字典,描述了如何配置对应的Handler实例。

    搜寻配置字典中的下述键:

    • class(强制性)。handler类的完全限定名。
    • level(可选)。handler的级别。
    • formatter(可选)。该handler的formatter的标识符。
    • filters(可选)。该handler的filter的标识符的列表。

    所有其它键被当作关键字参数传给handler的构造函数。例如,给定如下代码片段:

    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
    

    具有标识console的处理程序将使用sys.stdout作为基础流实例化为logging.StreamHandler标识符为file的handler初始化为logging.handlers.RotatingFileHandlerfilename='logconfig.log', maxBytes=1024, backupCount=3为其构造函数的参数。

  • loggers - 对应的值是个字典,其每一个键都是logger的名字,每一个值又是个字典,描述了如何配置对应的Logger实例。

    搜寻配置字典中的下述键:

    • level(可选)。logger的级别。
    • propagate(可选)。logger的传播设置。
    • filters(可选)。logger的filter的标识符的列表。
    • handlers(可选)。logger的handler的标识符的列表。

    指定的logger通过级别、传播、filter和handler来配置。

  • root - 根logger的配置。配置处理和其它logger一样,propagate设定不可用。

  • incremental - 配置是否是当前配置的增量配置。默认值为False,意味着指定的配置取代已存的配置,就和fileConfig()API的语义一样。

    如果为True,配置的处理描述于Incremental Configuration章节。

  • disable_existing_loggers - 是否禁用已存的logger。该设定模仿了fileConfig()同名参数的行为。如果缺少该参数,默认为True如果incrementalTrue,该值被忽略。

16.7.2.2. Incremental Configuration

很难为增量配置提供完整的弹性。例如,因为filter和formatter这样的对象是匿名的,一旦建立了配置,就很难在配置的时候去引用这些匿名对象。

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

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

配置可以以序列化的字典通过网络发往套接字监听器。因此,长时间运行的应用的日志冗余度可以随时间修改而不需要重启应用。

16.7.2.3. Object connections

模式描述了一组日志对象 - logger、handler、formatter、filter - 它们在一个对象图中彼此连接。因此模式需要表示对象间的连接。例如,一旦配置好,一个特定的logger会附上一个handler。为了便于讨论,我们定义两个对象的连接中,logger是源头,handler是目的地。在配置好的对象中,这表示logger持有handler的一个引用。为了达到这个目的,在配置字典中每个目的对象都有一个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.bazformatter和filter的id可以是任何的字符串(如上面的briefprecise)且是暂态的,意味着它们只在处理配置字典时有意义,用来决定对象间的连接,一旦配置结束,它们就不存在了。

上述片段表示名为foo.bar.baz的logger附有两个handler,分别是id为h1h2的handler来描述。h1的fotmatter由id为brief的formatter来描述,h2的formatter则是id为precise的formatter。

16.7.2.4. User-defined objects

该模式支持用户自定义handler、filter和formatter对象。(对于logger不需要不同的实例有不同的类型,所以该配置模式不支持用户定义的logger类。)

对象的配置是由字典来具体描述的。某些情况下日志系统可以从上下文推断出如何实例化一个对象,但是当实例化一个用户定义的对象的时候,系统就不知道怎么做了。为了为用户定义的对象的实例化提供完整的弹性,用户需要提供一个‘工厂’——一个可调用对象,调用时以配置字典为参数,返回实例化的对象。这由键'()'及对应的工厂的绝对导入路径来表示。这里是个具体的例子:

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片段定义了三个formatter。第一个,标识brief,是具有指定格式字符串的标准logging.Formatter实例。第二个,id default,具有更长的格式,并且还显式地定义时间格式,并且将导致使用这两个格式字符串初始化logging.Formatter以Python源码来看,formatter briefdefault有配置子字典:

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

和:

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

并且因为这些字典不包含特殊键'()',所以从上下文推断出实例化:作为结果,创建标准logging.Formatter实例。第三个formatter的id为custom,其配置子字典是:

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

它包含了特殊键'()',意味着要实例化一个用户定义的对象。这种情况下,使用指定的工厂可调用对象。如果它是一个真的可调用对象就直接使用它,否则如果和例子中一样,指明了一个字符串,将通过常规的导入机制来确定真正的可调用对象。配置子字典中剩余的的项目会被当成关键字参数来调用可调用对象。上述例子中,id为custom的formatter会是下面调用的返回值:

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

使用'()'作为特殊键是因为它不是个合法的关键字参数名,也就不会和调用时的关键字参数的名字冲突。'()'也用来表示对应值是个可调用对象。

16.7.2.5. Access to external objects

偶尔配置需要引用外部对象,比如sys.stderr如果配置dict是使用Python代码构造的,这是直接的,但是当通过文本文件(例如,文本文件)提供配置时出现问题。JSON,YAML)。在文本文件中,没有标准的方法来区分sys.stderr和字符串'sys.stderr'为了区分,配置系统会在字符串值中查找特定的特殊前缀,并对其特殊对待。例如,如果配置值为'ext://sys.stderr'ext://被移除,'sys.stderr'会被用来import。

The handling of such prefixes is done in a way analogous to protocol handling: there is a generic mechanism to look for prefixes which match the regular expression ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$ whereby, if the prefix is recognised, the suffix is processed in a prefix-dependent manner and the result of the processing replaces the string value. 如果不识别prefix,则字符串的在值保持不变。

16.7.2.6. Access to internal objects

和外部对象一样,偶尔需要引用配置中的内部对象。如果配置系统知道这些对象,这就会隐式的完成。例如,记录器或处理程序中level的字符串值'DEBUG'将自动转换为值logging.DEBUGhandlersfiltersformatter条目将获取对象标识并解析为适当的目标对象。

然而对于不知道logging模块的用户定义的对象来说就需要一个更通用的机制。考虑logging.handlers.MemoryHandler,它有target参数,是另一个handler的代理。既然系统知道此类,配置中对于target只要给出相关目标handler的对象id,系统会根据id解析出对应的handler对象。然而对于用户定义的my.package.MyHandler,它有一个alternate handler,系统就不知道alternate引用handler。为了满足此场景,通用的解析系统允许用户指定:

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字典中键为handlers的字典;依此类推。字符串'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']

16.7.2.7. Import resolution and custom importers

导入默认使用内置的__import__()函数。也许希望使用自己的导入机制:如果是这样,替换importer或者其超类DictConfiguratorBaseConfigurator属性。然而从类的描述符访问函数时要小心。如果使用Python可调用对象来做导入,而且希望在类级别而不是实例级别来定义它,使用staticmethod()包装它。例如:

from importlib import import_module
from logging.config import BaseConfigurator

BaseConfigurator.importer = staticmethod(import_module)

如果在配置器实例上设置导入可调用对象,不需要用staticmethod()来包装它。

16.7.3. Configuration file format

fileConfig()理解的配置文件格式基于configparser的功能。文件必需包含名为[loggers], [handlers][formatters]的节,标识了文件中定义的各种类型的对象的名字。对于每一个实体,有一个独立的节标识如何配置该实体。log01节中有名为[loggers]的logger,[logger_log01]的节中包含了相关的配置细节。类似的,hand01节中有名为[handlers]的handler,[handler_hand01]节中包含了相关的配置;form01节中有名为[formatters]的formatter,[formatter_form01]节中包含了相关的配置。[logger_root]节中包含了根logger的配置。

注意

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必需指定级别和handler列表。根logger节的例子给定如下。

[logger_root]
level=NOTSET
handlers=hand01

level可以是DEBUG, INFO, WARNING, ERROR, CRITICALNOTSET其中之一。NOTSET表示所有的消息都要记录,这只对根logger有效。级别值使用eval()logging包空间演算得到。

handlers是逗号分隔的handler名字的列表,这些必需出现在[handlers]节中。这些名字必需出现在[handlers]节中,且在配置文件中有对应的节。

对于非根logger的logger来说,需要一些额外的信息。如下例所示。

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

levelhandlers 的解读和根logger的一样,如果非根logger的级别为NOTSET,系统参考高层次的logger来决定logger的有效级别。propagate为1表示将消息传递给高层次logger的handler,为0表示传播。qualname是logger在层次中的名字,应用通过该名字得到logger。

handler配置节如下所示。

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

logging表示handler的类(在class包空间中通过eval()演算得到。)level的解读和logger的一样,NOTSET表示‘记录所有日志’。

formatter表示了该handler的formatter的名字。如果为空,使用默认formatter (logging._defaultFormatter)。如果指定了名字,它必需出现在[formatters]节中,且在配置文件中有对应的节。

logging是handler类构造函数的参数列表(在args包空间通过eval()演算得到)。参考相关handler的构造函数,或者参考下面的例子,以了解典型的构造。

[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是整体的格式化字符串,datefmtstrftime()兼容的日期/时间格式化字符串。如果为空,使用ISO8601格式,基本上等同于格式'%Y-%m-%d %H:%M:%S'ISO8601格式在上述格式的末尾指明了毫秒,中间有个逗号。ISO8601格式的例子:2003-01-23 00:29:50,411

class可选。它表示formatter类的名字(带点的模块和类名)。实例化Formatter子类时该选项很有用。Formatter子类可以用来表示详细/概要的异常回溯。

注意

如上所述由于使用了eval(),如果使用listen()来通过套接字发送和接受配置,这会导致潜在的安全风险。风险限于多个没有相互信任的用户在同一台​​机器上运行代码的地方;有关详细信息,请参阅listen()文档。

参见

模块logging
日志模块的API参考。
模块logging.handlers
日志模块所带的有用的handler。