Re: A new way to configure Python logging



On Sat, Oct 24, 2009 at 07:54 +0000, Vinay Sajip wrote:
Wolodja Wentland <wentland <at> cl.uni-heidelberg.de> writes:
[snip]
foo
|__bar
|__baz
|__newt
|___witch

I set every loggers log level to DEBUG and use the respective logger in

You only need set foo's level to DEBUG and all of foo.bar, foo.baz etc.
will inherit that level.
OK, thanks for pointing that out!

[snip]
Among other levels specific to the application, like PERFORMANCE for
performance related unit tests, ...

I'm not sure what you mean here - is it that you've defined a custom level
called PERFORMANCE?
Exactly. I used that particular level for logging within a unit test
framework for messages about performance related tests. Combined with a
Handler that generated HTML files from the LogRecord queue using various
templates (make, jinja, ...) it became a nice way to create nice looking
test reports.

Could a HTMLHandler be added to the standard set? Preferably one that
leaves the choice of the template engine to the user.

Application User Interface
[snip]
All of this sounds quite reasonable.
Great :-)

Implementation
--------------

You have rightfully noted in the PEP, that the ConfigParser method
is not really suitable for incremental configuration and I therefore
configure the logging system programmatically.

Since you allow users the ability to control logging from the command-line,
you need to do programmatic configuration anyway.
Yes, but that could be made easier. (see below)

I create all loggers with except the root (foo) with:

LOG = logging.getLogger(__name__)
LOG.setLevel(logging.DEBUG)

within each module and then register suitable handlers *with the root
logger* to process incoming LogRecords. That means that I usually have a
StreamHandler, a FileHandler among other more specific ones.

See my earlier comment about setting levels for each logger explicitly. How
do you avoid low-level chatter from all modules being displayed to users? Is
it through the use of Filters?

Exactly. The Handlers will usually employ elaborate filtering, so they
can be "plugged together" easily:

- User wants html? Ah, just add the HTMLHandler to the root logger
- User wants verbose output? Ah, just add the VerboseHandler to ...
- ...

There are times where specific handlers are attached lower down in the
logger hierarchy (e.g. a specific subsystem) to send information to a relevant
audience, e.g. the development or support team for that subsystem.

Guess I never had the need for that.

Technically you can achieve this by attaching everything to the root
and then attaching suitable Filters to those handlers, but it may be
easier in some cases to attach the handlers to a lower-level logger
directly, without the need for Filters.

Which is exactly what I do and I think that it fits my particular
mindset. I see the root handler basically as a multiplexer that feeds
LogRecords to various various co-routines (ie handlers) that decide what
to do with them. I like working on the complete set of LogRecords
accumulated from different parts of the application. The
handler/filter/... naming convention is just a more verbose/spelled out
way of defining different parts of the pipeline that the developer might
want to use. I guess I would welcome general purpose hook for each edge
in the logger tree and in particular one hook feeding different
co-routines at the root logger.

though your configuration would have to leave out any handlers which
are optionally specified via command-line arguments.
* Additionally: The possibility to *override* some parts of the
configuration in another file (files?).

That requirement is too broad to be able to give a one-size-fits-all
implementation.

I was thinking along the line of ConfigParser.read([file1, file2, ...]),
so that you could have:

--- /etc/foo/logging.conf ---
...
formatters:
default:
format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
...
--- snip ---

and:

--- ~/.foo/logging.conf ---
formatters:
# You can adapt the message and date format to your needs here.
# The following placeholder can be used:
# asctime - description
# ...

default:
format: '%(levelname)-8s %(name)-15s %(message)s'
datefmt: '%Y-%m-%d %H:%M:%S'
--- snip ---

So that if I call:

logging.config.fromFiles(['/etc/foo/logging.conf,
os.path.expanduser(
'~/.foo/logging.conf')])

The user adaptations will overrule the defaults in the shipped
configuration. I know that I could implement that myself using
{}.update() and the like, but the use case might be common enough to
justify inclusion in the logging module.

* The possibility to enable/disable certain parts of the configuration.

You can do that by changing levels in an incremental call. Can you give more
details about what else you might want to enable/disable?

I will give an example.. The basic problem I have with *all* config file
based configuration right now is that I have to *register* every single
handler/filter with a logger *within* the configuration or their
configuration will be lost.

Assume the following configuration:

--- snip ---
handlers:
h1: #This is an id
# configuration of handler with id h1 goes here
h2: #This is another id
# configuration of handler with id h2 goes here
loggers:
foo.bar.baz:
# other configuration for logger 'foo.bar.baz'
handlers: []
--- snip ---

In this configuration the handlers will be lost. There is no way to
retrieve he configured handlers later on. (or is there?).

What I would like to do is:

--- snip ---
...
if options.user_wants_h1:
try:
someLogger.addHandler(logging.getConfiguredHandler('h1'))
except HandlerNotFound as handler_err:
# handle exception

if options.user_wants_h2:
try:
someLogger.addHandler(logging.getConfiguredHandler('h2'))
except HandlerNotFound as handler_err:
# handle exception
--- snip ---

... same for loggers, filters, etc.

That would enable me to:

* Create a comprehensive logging building block configuration in its
entirety in a nice configuration format. (ie. config file)

* Easily combine these blocks programmatically

In a way I see three members to the party in the development/usage of
logging:

* Logging Expert

Will design the logging system for an application/library. Knows the
requirements and will be able to design different parts of the system.
She will then tell another developer (see below) which blocks are
available.

* Developer

A person that knows about the blocks and combines them
programmatically, designs the user interface and complains about
bugs/new requirements in/for the logging system to the "Logging
Expert".

* User

A user gets exposed to different ways in which to change the logging
system:

- command line options (switches to turn whole blocks off/on)
- configuration files

These *may* a subset of the configuration options that the developer
wants to expose to the user (format, dateformat, ...)
(see above)

Although I can see how configuration in general can benefit from a building-
block style approach (I developed an alternative hierarchical configuration
system, see http://www.red-dove.com/python_config.html - though that uses its
own JSON-like format and offers some nice features, it's not standard enough
to consider using for the logging package.)

Thanks for that link! I will certainly investigate that library.

The use of dicts means that users can combine portions of the final dict from
different sources, PEP391 doesn't prescribe exactly how this is done. The dict
presented to dictConfig() must be complete and consistent, but where all the
different bits come from is up to the application developer/system
administrator.

Which is one point I like about PEP 391. Just wanted to give some
feedback :-) . You can basically write everything yourself, it is just
that I think that a usage pattern that is frequently implemented on top
of a stdlib should be eventually incorporated into said library.

have a nice day

Wolodja

Attachment: signature.asc
Description: Digital signature



Relevant Pages

  • Re: A new way to configure Python logging
    ... Users can easily define their own handlers to do ... I think this sort of requirement varies sufficiently across developers ... and conventions make sense to them, and then expect logging to just follow ... instructions which are very specific to logging configuration, ...
    (comp.lang.python)
  • Re: A new way to configure Python logging
    ... post about your usage of logging. ... I usually register a logger 'foo' within the application and one logger ... you need to do programmatic configuration anyway. ... so that it is easy to just add multiple handlers for various levels to ...
    (comp.lang.python)
  • Logging with no handlers configured
    ... logger or its parents and you try to log events with that logger, ... No handlers could be found for logger ... logging or configure any handlers, the spurious message should not be ... the logging system will not give any notification. ...
    (comp.lang.python)
  • Re: A new way to configure Python logging
    ... layout and how incremental logging configuration should work, ... Once implemented, the configuration format will ... I usually register a logger 'foo' within the application and one logger ... The Handlers I register have suitable Filters associated with them, ...
    (comp.lang.python)
  • Re: Logging
    ... record of all the handlers and then calling flush on the handler ... followed by removeHandler from the logger. ... as a feature request for logging. ... Neil Benn ...
    (comp.lang.python)