2009-10-18 16:39:50 -04:00
|
|
|
|
PEP: 391
|
|
|
|
|
Title: Dictionary-Based Configuration For Logging
|
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Vinay Sajip <vinay_sajip at red-dove.com>
|
2011-08-30 06:50:41 -04:00
|
|
|
|
Status: Final
|
2009-10-18 16:39:50 -04:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 15-Oct-2009
|
|
|
|
|
Python-Version: 2.7, 3.2
|
|
|
|
|
Post-History:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
========
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
This PEP describes a new way of configuring logging using a dictionary
|
|
|
|
|
to hold configuration information.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
=========
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
The present means for configuring Python's logging package is either
|
|
|
|
|
by using the logging API to configure logging programmatically, or
|
|
|
|
|
else by means of ConfigParser-based configuration files.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Programmatic configuration, while offering maximal control, fixes the
|
2009-10-19 09:49:12 -04:00
|
|
|
|
configuration in Python code. This does not facilitate changing it
|
|
|
|
|
easily at runtime, and, as a result, the ability to flexibly turn the
|
|
|
|
|
verbosity of logging up and down for different parts of a using
|
|
|
|
|
application is lost. This limits the usability of logging as an aid
|
|
|
|
|
to diagnosing problems - and sometimes, logging is the only diagnostic
|
|
|
|
|
aid available in production environments.
|
|
|
|
|
|
|
|
|
|
The ConfigParser-based configuration system is usable, but does not
|
|
|
|
|
allow its users to configure all aspects of the logging package. For
|
|
|
|
|
example, Filters cannot be configured using this system. Furthermore,
|
|
|
|
|
the ConfigParser format appears to engender dislike (sometimes strong
|
|
|
|
|
dislike) in some quarters. Though it was chosen because it was the
|
|
|
|
|
only configuration format supported in the Python standard at that
|
|
|
|
|
time, many people regard it (or perhaps just the particular schema
|
|
|
|
|
chosen for logging's configuration) as 'crufty' or 'ugly', in some
|
|
|
|
|
cases apparently on purely aesthetic grounds.
|
|
|
|
|
|
|
|
|
|
Recent versions of Python include JSON support in the standard
|
|
|
|
|
library, and this is also usable as a configuration format. In other
|
|
|
|
|
environments, such as Google App Engine, YAML is used to configure
|
|
|
|
|
applications, and usually the configuration of logging would be
|
|
|
|
|
considered an integral part of the application configuration.
|
|
|
|
|
Although the standard library does not contain YAML support at
|
|
|
|
|
present, support for both JSON and YAML can be provided in a common
|
|
|
|
|
way because both of these serialization formats allow deserialization
|
2009-11-26 02:57:39 -05:00
|
|
|
|
to Python dictionaries.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
By providing a way to configure logging by passing the configuration
|
|
|
|
|
in a dictionary, logging will be easier to configure not only for
|
2009-11-26 02:57:39 -05:00
|
|
|
|
users of JSON and/or YAML, but also for users of custom configuration
|
2009-10-19 09:49:12 -04:00
|
|
|
|
methods, by providing a common format in which to describe the desired
|
|
|
|
|
configuration.
|
|
|
|
|
|
|
|
|
|
Another drawback of the current ConfigParser-based configuration
|
|
|
|
|
system is that it does not support incremental configuration: a new
|
|
|
|
|
configuration completely replaces the existing configuration.
|
|
|
|
|
Although full flexibility for incremental configuration is difficult
|
|
|
|
|
to provide in a multi-threaded environment, the new configuration
|
|
|
|
|
mechanism will allow the provision of limited support for incremental
|
|
|
|
|
configuration.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Specification
|
|
|
|
|
=============
|
|
|
|
|
|
|
|
|
|
The specification consists of two parts: the API and the format of the
|
2009-10-19 09:49:12 -04:00
|
|
|
|
dictionary used to convey configuration information (i.e. the schema
|
|
|
|
|
to which it must conform).
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Naming
|
|
|
|
|
------
|
|
|
|
|
|
2022-01-21 06:03:51 -05:00
|
|
|
|
Historically, the logging package has not been :pep:`8` conformant.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
At some future time, this will be corrected by changing method and
|
2022-01-21 06:03:51 -05:00
|
|
|
|
function names in the package in order to conform with :pep:`8`.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
API
|
|
|
|
|
---
|
|
|
|
|
|
2009-11-26 02:57:39 -05:00
|
|
|
|
The logging.config module will have the following addition:
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* A function, called ``dictConfig()``, which takes a single argument
|
2009-11-26 02:57:39 -05:00
|
|
|
|
- the dictionary holding the configuration. Exceptions will be
|
|
|
|
|
raised if there are errors while processing the dictionary.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-22 04:37:16 -04:00
|
|
|
|
It will be possible to customize this API - see the section on `API
|
2009-10-28 06:10:57 -04:00
|
|
|
|
Customization`_. `Incremental configuration`_ is covered in its own
|
|
|
|
|
section.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Dictionary Schema - Overview
|
|
|
|
|
----------------------------
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
Before describing the schema in detail, it is worth saying a few words
|
|
|
|
|
about object connections, support for user-defined objects and access
|
2009-10-20 04:11:52 -04:00
|
|
|
|
to external and internal objects.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Object connections
|
|
|
|
|
''''''''''''''''''
|
|
|
|
|
|
|
|
|
|
The schema is intended to describe a set of logging objects - loggers,
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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
|
2009-11-26 02:57:39 -05:00
|
|
|
|
particular logger has attached to it a particular handler. For the
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
So, for example, consider the following YAML snippet::
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-28 06:10:57 -04:00
|
|
|
|
formatters:
|
|
|
|
|
brief:
|
|
|
|
|
# configuration for formatter with id 'brief' goes here
|
|
|
|
|
precise:
|
|
|
|
|
# configuration for formatter with id 'precise' goes here
|
2009-10-19 09:49:12 -04:00
|
|
|
|
handlers:
|
|
|
|
|
h1: #This is an id
|
2009-10-28 06:10:57 -04:00
|
|
|
|
# configuration of handler with id 'h1' goes here
|
|
|
|
|
formatter: brief
|
2009-10-19 09:49:12 -04:00
|
|
|
|
h2: #This is another id
|
2009-10-28 06:10:57 -04:00
|
|
|
|
# configuration of handler with id 'h2' goes here
|
2017-03-24 17:11:33 -04:00
|
|
|
|
formatter: precise
|
2009-10-19 09:49:12 -04:00
|
|
|
|
loggers:
|
|
|
|
|
foo.bar.baz:
|
2009-10-20 04:11:52 -04:00
|
|
|
|
# other configuration for logger 'foo.bar.baz'
|
2009-10-19 09:49:12 -04:00
|
|
|
|
handlers: [h1, h2]
|
|
|
|
|
|
2009-10-20 04:11:52 -04:00
|
|
|
|
(Note: YAML will be used in this document as it is a little more
|
|
|
|
|
readable than the equivalent Python source form for the dictionary.)
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
The ids for loggers are the logger names which would be used
|
|
|
|
|
programmatically to obtain a reference to those loggers, e.g.
|
2009-10-28 06:10:57 -04:00
|
|
|
|
``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
|
2017-03-24 17:11:33 -04:00
|
|
|
|
`Handler Ids`_, below.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
|
|
|
|
The above snippet indicates that logger named ``foo.bar.baz`` should
|
|
|
|
|
have two handlers attached to it, which are described by the handler
|
2009-10-28 06:10:57 -04:00
|
|
|
|
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``.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
User-defined objects
|
|
|
|
|
''''''''''''''''''''
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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.
|
2009-11-26 02:57:39 -05:00
|
|
|
|
This will be signalled by an absolute import path to the factory being
|
|
|
|
|
made available under the special key ``'()'``. Here's a concrete
|
|
|
|
|
example::
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
|
|
|
|
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
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
The above YAML snippet defines three formatters. The first, with id
|
|
|
|
|
``brief``, is a standard ``logging.Formatter`` instance with the
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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'
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
and::
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
|
|
|
|
|
'datefmt' : '%Y-%m-%d %H:%M:%S'
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
respectively, and as these dictionaries do not contain the special key
|
|
|
|
|
``'()'``, the instantiation is inferred from the context: as a result,
|
2009-10-19 09:49:12 -04:00
|
|
|
|
standard ``logging.Formatter`` instances are created. The
|
|
|
|
|
configuration sub-dictionary for the third formatter, with id
|
|
|
|
|
``custom``, is::
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
'()' : 'my.package.customFormatterFactory',
|
|
|
|
|
'bar' : 'baz',
|
|
|
|
|
'spam' : 99.9,
|
|
|
|
|
'answer' : 42
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
and this contains the special key ``'()'``, which means that
|
|
|
|
|
user-defined instantiation is wanted. In this case, the specified
|
2009-11-26 02:57:39 -05:00
|
|
|
|
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::
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-20 04:11:52 -04:00
|
|
|
|
my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
|
|
|
|
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.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Access to external objects
|
|
|
|
|
''''''''''''''''''''''''''
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
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
|
2009-10-18 16:39:50 -04:00
|
|
|
|
as-is.
|
|
|
|
|
|
|
|
|
|
The implementation will provide for a set of standard prefixes such as
|
2009-10-19 09:49:12 -04:00
|
|
|
|
``ext://`` but it will be possible to disable the mechanism completely
|
|
|
|
|
or provide additional or different prefixes for special handling.
|
|
|
|
|
|
2009-10-28 06:10:57 -04:00
|
|
|
|
|
2009-10-20 04:11:52 -04:00
|
|
|
|
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
|
2017-03-24 17:11:33 -04:00
|
|
|
|
|
2009-10-20 04:11:52 -04:00
|
|
|
|
custom:
|
|
|
|
|
(): my.package.MyHandler
|
2009-11-24 01:52:39 -05:00
|
|
|
|
alternate: cfg://handlers.file
|
2009-10-20 04:11:52 -04:00
|
|
|
|
|
2009-11-24 01:52:39 -05:00
|
|
|
|
The literal string ``'cfg://handlers.file'`` will be resolved in an
|
2009-10-20 04:11:52 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2009-11-24 01:52:39 -05:00
|
|
|
|
in the configuration, the string ``'cfg://handlers'`` would resolve to
|
|
|
|
|
the dict with key ``handlers``, the string ``'cfg://handlers.email``
|
2009-10-20 04:11:52 -04:00
|
|
|
|
would resolve to the dict with key ``email`` in the ``handlers`` dict,
|
2009-11-24 01:52:39 -05:00
|
|
|
|
and so on. The string ``'cfg://handlers.email.toaddrs[1]`` would
|
2009-10-20 04:11:52 -04:00
|
|
|
|
resolve to ``'dev_team.domain.tld'`` and the string
|
2009-11-24 01:52:39 -05:00
|
|
|
|
``'cfg://handlers.email.toaddrs[0]'`` would resolve to the value
|
2009-10-20 04:11:52 -04:00
|
|
|
|
``'support_team@domain.tld'``. The ``subject`` value could be accessed
|
2009-11-24 01:52:39 -05:00
|
|
|
|
using either ``'cfg://handlers.email.subject'`` or, equivalently,
|
|
|
|
|
``'cfg://handlers.email[subject]'``. The latter form only needs to be
|
2009-10-20 04:11:52 -04:00
|
|
|
|
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.
|
|
|
|
|
|
2009-11-24 01:52:39 -05:00
|
|
|
|
Given a string ``cfg://handlers.myhandler.mykey.123``, this will
|
2009-10-22 04:06:15 -04:00
|
|
|
|
resolve to ``config_dict['handlers']['myhandler']['mykey']['123']``.
|
2009-11-24 01:52:39 -05:00
|
|
|
|
If the string is specified as ``cfg://handlers.myhandler.mykey[123]``,
|
2009-10-22 04:06:15 -04:00
|
|
|
|
the system will attempt to retrieve the value from
|
2009-11-24 01:52:39 -05:00
|
|
|
|
``config_dict['handlers']['myhandler']['mykey'][123]``, and fall back
|
2009-10-22 04:06:15 -04:00
|
|
|
|
to ``config_dict['handlers']['myhandler']['mykey']['123']`` if that
|
|
|
|
|
fails.
|
|
|
|
|
|
2009-10-28 06:10:57 -04:00
|
|
|
|
|
|
|
|
|
Handler Ids
|
|
|
|
|
'''''''''''
|
|
|
|
|
|
|
|
|
|
Some specific logging configurations require the use of handler levels
|
|
|
|
|
to achieve the desired effect. However, unlike loggers which can
|
|
|
|
|
always be identified by their names, handlers have no persistent
|
|
|
|
|
handles whereby levels can be changed via an incremental configuration
|
|
|
|
|
call.
|
|
|
|
|
|
|
|
|
|
Therefore, this PEP proposes to add an optional ``name`` property to
|
|
|
|
|
handlers. If used, this will add an entry in a dictionary which maps
|
|
|
|
|
the name to the handler. (The entry will be removed when the handler
|
|
|
|
|
is closed.) When an incremental configuration call is made, handlers
|
|
|
|
|
will be looked up in this dictionary to set the handler level
|
|
|
|
|
according to the value in the configuration. See the section on
|
|
|
|
|
`incremental configuration`_ for more details.
|
|
|
|
|
|
|
|
|
|
In theory, such a "persistent name" facility could also be provided
|
|
|
|
|
for Filters and Formatters. However, there is not a strong case to be
|
|
|
|
|
made for being able to configure these incrementally. On the basis
|
|
|
|
|
that practicality beats purity, only Handlers will be given this new
|
|
|
|
|
``name`` property. The id of a handler in the configuration will
|
|
|
|
|
become its ``name``.
|
|
|
|
|
|
|
|
|
|
The handler name lookup dictionary is for configuration use only and
|
|
|
|
|
will not become part of the public API for the package.
|
|
|
|
|
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
Dictionary Schema - Detail
|
|
|
|
|
--------------------------
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
The dictionary passed to ``dictConfig()`` must contain the following
|
|
|
|
|
keys:
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
* `version` - to be set to an integer value representing the schema
|
2009-10-19 09:49:12 -04:00
|
|
|
|
version. The only valid value at present is 1, but having this key
|
|
|
|
|
allows the schema to evolve while still preserving backwards
|
|
|
|
|
compatibility.
|
|
|
|
|
|
|
|
|
|
All other keys are optional, but if present they will be interpreted
|
|
|
|
|
as described below. In all cases below where a 'configuring dict' is
|
|
|
|
|
mentioned, it will be checked for the special ``'()'`` key to see if a
|
|
|
|
|
custom instantiation is required. If so, the mechanism described
|
|
|
|
|
above is used to instantiate; otherwise, the context is used to
|
|
|
|
|
determine how to instantiate.
|
|
|
|
|
|
|
|
|
|
* `formatters` - the corresponding value will be a dict in which each
|
|
|
|
|
key is a formatter id and each value is a dict describing how to
|
|
|
|
|
configure the corresponding Formatter instance.
|
|
|
|
|
|
|
|
|
|
The configuring dict is searched for keys ``format`` and ``datefmt``
|
|
|
|
|
(with defaults of ``None``) and these are used to construct a
|
2009-10-18 16:39:50 -04:00
|
|
|
|
``logging.Formatter`` instance.
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* `filters` - the corresponding value will be a dict in which each key
|
|
|
|
|
is a filter id and each value is a dict describing how to configure
|
|
|
|
|
the corresponding Filter instance.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
The configuring dict is searched for key ``name`` (defaulting to the
|
|
|
|
|
empty string) and this is used to construct a ``logging.Filter``
|
|
|
|
|
instance.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* `handlers` - the corresponding value will be a dict in which each
|
|
|
|
|
key is a handler id and each value is a dict describing how to
|
|
|
|
|
configure the corresponding Handler instance.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
The configuring dict is searched for the following keys:
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* ``class`` (mandatory). This is the fully qualified name of the
|
|
|
|
|
handler class.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* ``level`` (optional). The level of the handler.
|
|
|
|
|
|
|
|
|
|
* ``formatter`` (optional). The id of the formatter for this
|
|
|
|
|
handler.
|
|
|
|
|
|
|
|
|
|
* ``filters`` (optional). A list of ids of the filters for this
|
|
|
|
|
handler.
|
|
|
|
|
|
|
|
|
|
All *other* keys are passed through as keyword arguments to the
|
|
|
|
|
handler's constructor. For example, given the snippet::
|
|
|
|
|
|
|
|
|
|
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
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
the handler with id ``console`` is instantiated as a
|
2009-10-19 09:49:12 -04:00
|
|
|
|
``logging.StreamHandler``, using ``sys.stdout`` as the underlying
|
|
|
|
|
stream. The handler with id ``file`` is instantiated as a
|
2009-10-18 16:39:50 -04:00
|
|
|
|
``logging.handlers.RotatingFileHandler`` with the keyword arguments
|
2009-10-20 04:11:52 -04:00
|
|
|
|
``filename='logconfig.log', maxBytes=1024, backupCount=3``.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* `loggers` - the corresponding value will be a dict in which each key
|
|
|
|
|
is a logger name and each value is a dict describing how to
|
|
|
|
|
configure the corresponding Logger instance.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
The configuring dict is searched for the following keys:
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
* ``level`` (optional). The level of the logger.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
* ``propagate`` (optional). The propagation setting of the logger.
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* ``filters`` (optional). A list of ids of the filters for this
|
|
|
|
|
logger.
|
|
|
|
|
|
|
|
|
|
* ``handlers`` (optional). A list of ids of the handlers for this
|
|
|
|
|
logger.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
The specified loggers will be configured according to the level,
|
|
|
|
|
propagation, filters and handlers specified.
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* `root` - this will be the configuration for the root logger.
|
|
|
|
|
Processing of the configuration will be as for any logger, except
|
|
|
|
|
that the ``propagate`` setting will not be applicable.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
* `incremental` - whether the configuration is to be interpreted as
|
2009-10-19 09:49:12 -04:00
|
|
|
|
incremental to the existing configuration. This value defaults to
|
|
|
|
|
``False``, which means that the specified configuration replaces the
|
|
|
|
|
existing configuration with the same semantics as used by the
|
|
|
|
|
existing ``fileConfig()`` API.
|
|
|
|
|
|
|
|
|
|
If the specified value is ``True``, the configuration is processed
|
|
|
|
|
as described in the section on `Incremental Configuration`_, below.
|
|
|
|
|
|
2009-11-24 01:52:39 -05:00
|
|
|
|
* `disable_existing_loggers` - whether any existing loggers are to be
|
|
|
|
|
disabled. This setting mirrors the parameter of the same name in
|
|
|
|
|
``fileConfig()``. If absent, this parameter defaults to ``True``.
|
|
|
|
|
This value is ignored if `incremental` is ``True``.
|
2017-03-24 17:11:33 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
A Working Example
|
|
|
|
|
-----------------
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
The following is an actual working configuration in YAML format
|
|
|
|
|
(except that the email addresses are bogus)::
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
formatters:
|
|
|
|
|
brief:
|
|
|
|
|
format: '%(levelname)-8s: %(name)-15s: %(message)s'
|
|
|
|
|
precise:
|
|
|
|
|
format: '%(asctime)s %(name)-15s %(levelname)-8s %(message)s'
|
|
|
|
|
filters:
|
|
|
|
|
allow_foo:
|
|
|
|
|
name: foo
|
|
|
|
|
handlers:
|
|
|
|
|
console:
|
|
|
|
|
class : logging.StreamHandler
|
|
|
|
|
formatter: brief
|
|
|
|
|
level : INFO
|
|
|
|
|
stream : ext://sys.stdout
|
|
|
|
|
filters: [allow_foo]
|
|
|
|
|
file:
|
|
|
|
|
class : logging.handlers.RotatingFileHandler
|
|
|
|
|
formatter: precise
|
|
|
|
|
filename: logconfig.log
|
|
|
|
|
maxBytes: 1024
|
|
|
|
|
backupCount: 3
|
|
|
|
|
debugfile:
|
|
|
|
|
class : logging.FileHandler
|
|
|
|
|
formatter: precise
|
|
|
|
|
filename: logconfig-detail.log
|
|
|
|
|
mode: a
|
|
|
|
|
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.
|
|
|
|
|
loggers:
|
|
|
|
|
foo:
|
|
|
|
|
level : ERROR
|
|
|
|
|
handlers: [debugfile]
|
|
|
|
|
spam:
|
|
|
|
|
level : CRITICAL
|
|
|
|
|
handlers: [debugfile]
|
|
|
|
|
propagate: no
|
|
|
|
|
bar.baz:
|
|
|
|
|
level: WARNING
|
|
|
|
|
root:
|
|
|
|
|
level : DEBUG
|
|
|
|
|
handlers : [console, file]
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
Incremental Configuration
|
|
|
|
|
=========================
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
It is difficult to provide complete flexibility for incremental
|
2009-10-28 06:10:57 -04:00
|
|
|
|
configuration. For example, because objects such as filters
|
2009-10-19 09:49:12 -04:00
|
|
|
|
and formatters are anonymous, once a configuration is set up, it is
|
|
|
|
|
not possible to refer to such anonymous objects when augmenting a
|
2009-10-28 06:10:57 -04:00
|
|
|
|
configuration.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
Furthermore, there is not a compelling case for arbitrarily altering
|
|
|
|
|
the object graph of loggers, handlers, filters, formatters at
|
2009-10-28 06:10:57 -04:00
|
|
|
|
run-time, once a configuration is set up; the verbosity of loggers and
|
|
|
|
|
handlers can be controlled just by setting levels (and, in the case of
|
2009-10-28 06:19:38 -04:00
|
|
|
|
loggers, propagation flags). Changing the object graph arbitrarily in
|
|
|
|
|
a safe way is problematic in a multi-threaded environment; while not
|
|
|
|
|
impossible, the benefits are not worth the complexity it adds to the
|
|
|
|
|
implementation.
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
|
|
|
|
Thus, when the ``incremental`` key of a configuration dict is present
|
2009-10-28 06:10:57 -04:00
|
|
|
|
and is ``True``, the system will ignore any ``formatters`` and
|
2009-10-28 06:21:10 -04:00
|
|
|
|
``filters`` entries completely, and process only the ``level``
|
2009-10-28 06:10:57 -04:00
|
|
|
|
settings in the ``handlers`` entries, and the ``level`` and
|
|
|
|
|
``propagate`` settings in the ``loggers`` and ``root`` entries.
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-22 04:06:15 -04:00
|
|
|
|
It's certainly possible to provide incremental configuration by other
|
|
|
|
|
means, for example making ``dictConfig()`` take an ``incremental``
|
|
|
|
|
keyword argument which defaults to ``False``. The reason for
|
2009-11-24 01:52:39 -05:00
|
|
|
|
suggesting that a value in the configuration dict be used is that it
|
2009-10-22 04:06:15 -04:00
|
|
|
|
allows for configurations to be sent over the wire as pickled dicts
|
|
|
|
|
to a socket listener. Thus, the logging verbosity of a long-running
|
|
|
|
|
application can be altered over time with no need to stop and
|
|
|
|
|
restart the application.
|
|
|
|
|
|
|
|
|
|
Note: Feedback on incremental configuration needs based on your
|
|
|
|
|
practical experience will be particularly welcome.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
API Customization
|
|
|
|
|
=================
|
|
|
|
|
|
|
|
|
|
The bare-bones ``dictConfig()`` API will not be sufficient for all
|
|
|
|
|
use cases. Provision for customization of the API will be made by
|
|
|
|
|
providing the following:
|
|
|
|
|
|
|
|
|
|
* A class, called ``DictConfigurator``, whose constructor is passed
|
|
|
|
|
the dictionary used for configuration, and which has a
|
|
|
|
|
``configure()`` method.
|
|
|
|
|
|
|
|
|
|
* A callable, called ``dictConfigClass``, which will (by default) be
|
|
|
|
|
set to ``DictConfigurator``. This is provided so that if desired,
|
|
|
|
|
``DictConfigurator`` can be replaced with a suitable user-defined
|
|
|
|
|
implementation.
|
|
|
|
|
|
|
|
|
|
The ``dictConfig()`` function will call ``dictConfigClass`` passing
|
|
|
|
|
the specified dictionary, and then call the ``configure()`` method on
|
|
|
|
|
the returned object to actually put the configuration into effect::
|
|
|
|
|
|
|
|
|
|
def dictConfig(config):
|
|
|
|
|
dictConfigClass(config).configure()
|
|
|
|
|
|
|
|
|
|
This should cater to all customization needs. 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``. The ``dictConfigClass``
|
|
|
|
|
would be bound to the subclass, and then ``dictConfig()`` could be
|
|
|
|
|
called exactly as in the default, uncustomized state.
|
|
|
|
|
|
2009-11-25 14:06:08 -05:00
|
|
|
|
Change to Socket Listener Implementation
|
|
|
|
|
========================================
|
|
|
|
|
|
|
|
|
|
The existing socket listener implementation will be modified as
|
|
|
|
|
follows: when a configuration message is received, an attempt will be
|
|
|
|
|
made to deserialize to a dictionary using the json module. If this
|
|
|
|
|
step fails, the message will be assumed to be in the fileConfig format
|
2009-11-26 02:57:39 -05:00
|
|
|
|
and processed as before. If deserialization is successful, then
|
2009-11-25 14:06:08 -05:00
|
|
|
|
``dictConfig()`` will be called to process the resulting dictionary.
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
Configuration Errors
|
|
|
|
|
====================
|
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
If an error is encountered during configuration, the system will raise
|
2009-10-22 04:10:22 -04:00
|
|
|
|
a ``ValueError``, ``TypeError``, ``AttributeError`` or ``ImportError``
|
2009-10-20 04:11:52 -04:00
|
|
|
|
with a suitably descriptive message. The following is a (possibly
|
|
|
|
|
incomplete) list of conditions which will raise an error:
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* A ``level`` which is not a string or which is a string not
|
|
|
|
|
corresponding to an actual logging level
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
2009-10-19 09:49:12 -04:00
|
|
|
|
* A ``propagate`` value which is not a boolean
|
2009-10-18 16:39:50 -04:00
|
|
|
|
|
|
|
|
|
* An id which does not have a corresponding destination
|
|
|
|
|
|
2009-10-28 06:10:57 -04:00
|
|
|
|
* A non-existent handler id found during an incremental call
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
* An invalid logger name
|
|
|
|
|
|
2009-10-20 04:11:52 -04:00
|
|
|
|
* Inability to resolve to an internal or external object
|
2009-10-19 09:49:12 -04:00
|
|
|
|
|
2009-10-22 04:06:15 -04:00
|
|
|
|
|
2009-11-25 15:57:24 -05:00
|
|
|
|
Discussion in the community
|
|
|
|
|
===========================
|
|
|
|
|
|
|
|
|
|
The PEP has been announced on python-dev and python-list. While there
|
|
|
|
|
hasn't been a huge amount of discussion, this is perhaps to be
|
|
|
|
|
expected for a niche topic.
|
|
|
|
|
|
|
|
|
|
Discussion threads on python-dev:
|
|
|
|
|
|
2017-06-11 15:02:39 -04:00
|
|
|
|
https://mail.python.org/pipermail/python-dev/2009-October/092695.html
|
|
|
|
|
https://mail.python.org/pipermail/python-dev/2009-October/092782.html
|
|
|
|
|
https://mail.python.org/pipermail/python-dev/2009-October/093062.html
|
2009-11-25 15:57:24 -05:00
|
|
|
|
|
|
|
|
|
And on python-list:
|
|
|
|
|
|
2017-06-11 15:02:39 -04:00
|
|
|
|
https://mail.python.org/pipermail/python-list/2009-October/1223658.html
|
|
|
|
|
https://mail.python.org/pipermail/python-list/2009-October/1224228.html
|
2009-11-25 15:57:24 -05:00
|
|
|
|
|
|
|
|
|
There have been some comments in favour of the proposal, no
|
|
|
|
|
objections to the proposal as a whole, and some questions and
|
|
|
|
|
objections about specific details. These are believed by the author
|
|
|
|
|
to have been addressed by making changes to the PEP.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reference implementation
|
|
|
|
|
========================
|
|
|
|
|
|
|
|
|
|
A reference implementation of the changes is available as a module
|
|
|
|
|
dictconfig.py with accompanying unit tests in test_dictconfig.py, at:
|
|
|
|
|
|
|
|
|
|
http://bitbucket.org/vinay.sajip/dictconfig
|
|
|
|
|
|
|
|
|
|
This incorporates all features other than the socket listener change.
|
|
|
|
|
|
|
|
|
|
|
2009-10-18 16:39:50 -04:00
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
coding: utf-8
|
|
|
|
|
End:
|