The specification consists of two parts: the API and the format of the dictionary used to convey configuration information (i.e. the schema to which it must conform).

Historically, the logging package has not been PEP 8 conformant . At some future time, this will be corrected by changing method and function names in the package in order to conform with PEP 8 . However, in the interests of uniformity, the proposed additions to the API use a naming scheme which is consistent with the present scheme used by logging.

It will be possible to customize this API - see the section on API Customization . Incremental configuration is covered in its own section.

Before describing the schema in detail, it is worth saying a few words about object connections, support for user-defined objects and access to external and internal objects.

Object connections The schema is intended to describe a set of logging objects - loggers, handlers, formatters, filters - which are connected to each other in an object graph. Thus, the schema needs to represent connections between the objects. For example, say that, once configured, a particular logger has attached to it a particular handler. For the purposes of this discussion, we can say that the logger represents the source, and the handler the destination, of a connection between the two. Of course in the configured objects this is represented by the logger holding a reference to the handler. In the configuration dict, this is done by giving each destination object an id which identifies it unambiguously, and then using the id in the source object's configuration to indicate that a connection exists between the source and the destination object with that id. So, for example, consider the following YAML snippet: 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] (Note: YAML will be used in this document as it is a little more readable than the equivalent Python source form for the dictionary.) The ids for loggers are the logger names which would be used programmatically to obtain a reference to those loggers, e.g. foo.bar.baz . The ids for Formatters and Filters can be any string value (such as brief , precise above) and they are transient, in that they are only meaningful for processing the configuration dictionary and used to determine connections between objects, and are not persisted anywhere when the configuration call is complete. Handler ids are treated specially, see the section on Handler Ids, below. The above snippet indicates that logger named foo.bar.baz should have two handlers attached to it, which are described by the handler ids h1 and h2 . The formatter for h1 is that described by id brief , and the formatter for h2 is that described by id precise .

User-defined objects The schema should support user-defined objects for handlers, filters and formatters. (Loggers do not need to have different types for different instances, so there is no support - in the configuration - for user-defined logger classes.) Objects to be configured will typically be described by dictionaries which detail their configuration. In some places, the logging system will be able to infer from the context how an object is to be instantiated, but when a user-defined object is to be instantiated, the system will not know how to do this. In order to provide complete flexibility for user-defined object instantiation, the user will need to provide a 'factory' - a callable which is called with a configuration dictionary and which returns the instantiated object. This will be signalled by an absolute import path to the factory being made available under the special key '()' . Here's a concrete example: 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 The above YAML snippet defines three formatters. The first, with id brief , is a standard logging.Formatter instance with the specified format string. The second, with id default , has a longer format and also defines the time format explicitly, and will result in a logging.Formatter initialized with those two format strings. Shown in Python source form, the brief and default formatters have configuration sub-dictionaries: { 'format' : '%(message)s' } and: { 'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s', 'datefmt' : '%Y-%m-%d %H:%M:%S' } respectively, and as these dictionaries do not contain the special key '()' , the instantiation is inferred from the context: as a result, standard logging.Formatter instances are created. The configuration sub-dictionary for the third formatter, with id custom , is: { '()' : 'my.package.customFormatterFactory', 'bar' : 'baz', 'spam' : 99.9, 'answer' : 42 } and this contains the special key '()' , which means that user-defined instantiation is wanted. In this case, the specified factory callable will be used. If it is an actual callable it will be used directly - otherwise, if you specify a string (as in the example) the actual callable will be located using normal import mechanisms. The callable will be called with the remaining items in the configuration sub-dictionary as keyword arguments. In the above example, the formatter with id custom will be assumed to be returned by the call: my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42) The key '()' has been used as the special key because it is not a valid keyword parameter name, and so will not clash with the names of the keyword arguments used in the call. The '()' also serves as a mnemonic that the corresponding value is a callable.

Access to external objects There are times where a configuration will need to refer to objects external to the configuration, for example sys.stderr . If the configuration dict is constructed using Python code then this is straightforward, but a problem arises when the configuration is provided via a text file (e.g. JSON, YAML). In a text file, there is no standard way to distinguish sys.stderr from the literal string 'sys.stderr' . To facilitate this distinction, the configuration system will look for certain special prefixes in string values and treat them specially. For example, if the literal string 'ext://sys.stderr' is provided as a value in the configuration, then the ext:// will be stripped off and the remainder of the value processed using normal import mechanisms. The handling of such prefixes will be done in a way analogous to protocol handling: there will be 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. If the prefix is not recognised, then the string value will be left as-is. The implementation will provide for a set of standard prefixes such as ext:// but it will be possible to disable the mechanism completely or provide additional or different prefixes for special handling.

Access to internal objects As well as external objects, there is sometimes also a need to refer to objects in the configuration. This will be done implicitly by the configuration system for things that it knows about. For example, the string value 'DEBUG' for a level in a logger or handler will automatically be converted to the value logging.DEBUG , and the handlers , filters and formatter entries will take an object id and resolve to the appropriate destination object. However, a more generic mechanism needs to be provided for the case of user-defined objects which are not known to logging. For example, take the instance of logging.handlers.MemoryHandler , which takes a target which is another handler to delegate to. Since the system already knows about this class, then in the configuration, the given target just needs to be the object id of the relevant target handler, and the system will resolve to the handler from the id. If, however, a user defines a my.package.MyHandler which has a alternate handler, the configuration system would not know that the alternate referred to a handler. To cater for this, a generic resolution system will be provided which allows the user to specify: handlers: file: # configuration of file handler goes here custom: (): my.package.MyHandler alternate: cfg://handlers.file The literal string 'cfg://handlers.file' will be resolved in an analogous way to the strings with the ext:// prefix, but looking in the configuration itself rather than the import namespace. The mechanism will allow access by dot or by index, in a similar way to that provided by str.format . Thus, given the following snippet: 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. in the configuration, the string 'cfg://handlers' would resolve to the dict with key handlers , the string 'cfg://handlers.email would resolve to the dict with key email in the handlers dict, and so on. The string 'cfg://handlers.email.toaddrs[1] would resolve to 'dev_team.domain.tld' and the string 'cfg://handlers.email.toaddrs[0]' would resolve to the value 'support_team@domain.tld' . The subject value could be accessed using either 'cfg://handlers.email.subject' or, equivalently, 'cfg://handlers.email[subject]' . The latter form only needs to be used if the key contains spaces or non-alphanumeric characters. If an index value consists only of decimal digits, access will be attempted using the corresponding integer value, falling back to the string value if needed. Given a string cfg://handlers.myhandler.mykey.123 , this will resolve to config_dict['handlers']['myhandler']['mykey']['123'] . If the string is specified as cfg://handlers.myhandler.mykey[123] , the system will attempt to retrieve the value from config_dict['handlers']['myhandler']['mykey'][123] , and fall back to config_dict['handlers']['myhandler']['mykey']['123'] if that fails.