py.log documentation and musings¶
This document is an attempt to briefly state the actual specification of the
py.log module. It was written by Francois Pinard and also contains
some ideas for enhancing the py.log facilities.
py.log is subject to refactorings, it may change with
the next release.
This document is meant to trigger or facilitate discussions. It shamelessly steals from the Agile Testing comments, and from other sources as well, without really trying to sort them out.
py.log module aims a niche comparable to the one of the
logging module found within the standard Python distributions, yet
with much simpler paradigms for configuration and usage.
Holger Krekel, the main
py library developer, introduced
the idea of keyword-based logging and the idea of logging producers and
consumers. A log producer is an object used by the application code
to send messages to various log consumers. When you create a log
producer, you define a set of keywords that are then used to both route
the logging messages to consumers, and to prefix those messages.
In fact, each log producer has a few keywords associated with it for identification purposes. These keywords form a tuple of strings, and may be used to later retrieve a particular log producer.
A log producer may (or may not) be associated with a log consumer, meant
to handle log messages in particular ways. The log consumers can be
STDERR, log files, syslog, the Windows Event Log, user
defined functions, etc. (Yet, logging to syslog or to the Windows Event
Log is only future plans for now). A log producer has never more than
one consumer at a given time, but it is possible to dynamically switch
a producer to use another consumer. On the other hand, a single log
consumer may be associated with many producers.
Note that creating and associating a producer and a consumer is done
automatically when not otherwise overriden, so using
is quite comfortable even in the smallest programs. More typically,
the application programmer will likely design a hierarchy of producers,
and will select keywords appropriately for marking the hierarchy tree.
If a node of the hierarchical tree of producers has to be divided in
sub-trees, all producers in the sub-trees share, as a common prefix, the
keywords of the node being divided. In other words, we go further down
in the hierarchy of producers merely by adding keywords.
Using the py.log library¶
To use the
py.log library, the user must import it into a Python
application, create at least one log producer and one log consumer, have
producers and consumers associated, and finally call the log producers
as needed, giving them log messages.
py library is installed on your system, a mere:
holds enough magic for lazily importing the various facilities of the
py library when they are first needed. This is really how
py.log is made available to the application. For example, after
import py, one may directly write
and everything should work fine, the user does not have to worry about
specifically importing more modules.
Creating a producer¶
There are three ways for creating a log producer instance:
- As soon as
py.logis first evaluated within an application program, a default log producer is created, and made available under the name
py.log.default. The keyword
defaultis associated with that producer.
py.log.Producer()constructor may be explicitly called for creating a new instance of a log producer. That constructor accepts, as an argument, the keywords that should be associated with that producer. Keywords may be given either as a tuple of keyword strings, or as a single space-separated string of keywords.
- Whenever an attribute is taken out of a log producer instance, for the first time that attribute is taken, a new log producer is created. The keywords associated with that new producer are those of the initial producer instance, to which is appended the name of the attribute being taken.
The last point is especially useful, as it allows using log producers without further declarations, merely creating them on-the-fly.
Creating a consumer¶
There are many ways for creating or denoting a log consumer:
A default consumer exists within the
py.logfacilities, which has the effect of writing log messages on the Python standard output stream. That consumer is associated at the very top of the producer hierarchy, and as such, is called whenever no other consumer is found.
py.log.STDOUTaccesses a log consumer which writes log messages on the Python standard output stream.
py.log.STDERRaccesses a log consumer which writes log messages on the Python standard error stream.
py.log.File()constructor accepts, as argument, either a file already opened in write mode or any similar file-like object, and creates a log consumer able to write log messages onto that file.
py.log.Path()constructor accepts a file name for its first argument, and creates a log consumer able to write log messages into that file. The constructor call accepts a few keyword parameters:
append, which is
Falseby default, may be used for opening the file in append mode instead of write mode.
delayed_create, which is
Falseby default, maybe be used for opening the file at the latest possible time. Consequently, the file will not be created if it did not exist, and no actual log message gets written to it.
buffering, which is 1 by default, is used when opening the file. Buffering can be turned off by specifying a 0 value. The buffer size may also be selected through this argument.
Any user defined function may be used for a log consumer. Such a function should accept a single argument, which is the message to write, and do whatever is deemed appropriate by the programmer. When the need arises, this may be an especially useful and flexible feature.
The special value
Nonemeans no consumer at all. This acts just like if there was a consumer which would silently discard all log messages sent to it.
Associating producers and consumers¶
Each log producer may have at most one log consumer associated with
it. A log producer gets associated with a log consumer through a
py.log.setconsumer() call. That function accepts two arguments,
the first identifying a producer (a tuple of keyword strings or a single
space-separated string of keywords), the second specifying the precise
consumer to use for that producer. Until this function is called for a
producer, that producer does not have any explicit consumer associated
Now, the hierarchy of log producers establishes which consumer gets used whenever a producer has no explicit consumer. When a log producer has no consumer explicitly associated with it, it dynamically and recursively inherits the consumer of its parent node, that is, that node being a bit closer to the root of the hierarchy. In other words, the rightmost keywords of that producer are dropped until another producer is found which has an explicit consumer. A nice side-effect is that, by explicitly associating a consumer with a producer, all consumer-less producers which appear under that producer, in the hierarchy tree, automatically inherits that consumer.
Writing log messages¶
All log producer instances are also functions, and this is by calling them that log messages are generated. Each call to a producer object produces the text for one log entry, which in turn, is sent to the log consumer for that producer.
The log entry displays, after a prefix identifying the log producer
being used, all arguments given in the call, converted to strings and
space-separated. (This is meant by design to be fairly similar to what
Note that the consumer is responsible for adding the newline at the end of the log entry. That final newline is not part of the text for the log entry.