[Python-checkins] python/nondist/peps pep-0282.txt,1.2,1.3
bwarsaw@users.sourceforge.net
bwarsaw@users.sourceforge.net
Mon, 08 Jul 2002 09:50:46 -0700
Update of /cvsroot/python/python/nondist/peps
In directory usw-pr-cvs1:/tmp/cvs-serv14349
Modified Files:
pep-0282.txt
Log Message:
Vinay's update, with lots of reformatting by Barry.
Index: pep-0282.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0282.txt,v
retrieving revision 1.2
retrieving revision 1.3
diff -C2 -d -r1.2 -r1.3
*** pep-0282.txt 23 Jun 2002 23:58:18 -0000 1.2
--- pep-0282.txt 8 Jul 2002 16:50:43 -0000 1.3
***************
*** 3,7 ****
Version: $Revision$
Last-Modified: $Date$
! Author: vinay_sajip@red-dove.com (Vinay Sajip)
Status: Draft
Type: Standards Track
--- 3,7 ----
Version: $Revision$
Last-Modified: $Date$
! Author: vinay_sajip at red-dove.com (Vinay Sajip)
Status: Draft
Type: Standards Track
***************
*** 16,25 ****
standard library.
! Basically the system involves the user creating one or more
! logging objects on which methods are called to log debugging
! notes/general information/warnings/errors/etc. Different logging
! 'levels' can be used to distinguish important messages from
! trivial ones.
!
A registry of named singleton logger objects is maintained so that
--- 16,25 ----
standard library.
! Basically the system involves the user creating one or more logger
! objects on which methods are called to log debugging notes,
! general information, warnings, errors etc. Different logging
! 'levels' can be used to distinguish important messages from less
! important ones.
!
A registry of named singleton logger objects is maintained so that
***************
*** 27,31 ****
(say, one for 'zope.zodb' stuff and another for
'mywebsite'-specific stuff)
!
2) one does not have to pass logger object references around.
--- 27,31 ----
(say, one for 'zope.zodb' stuff and another for
'mywebsite'-specific stuff)
!
2) one does not have to pass logger object references around.
***************
*** 34,38 ****
while not touching the application itself.
!
Motivation
--- 34,38 ----
while not touching the application itself.
!
Motivation
***************
*** 45,114 ****
Influences
! This proposal was put together after having somewhat studied the
following logging packages:
o java.util.logging in JDK 1.4 (a.k.a. JSR047) [1]
o log4j [2]
- These two systems are *very* similar.
o the Syslog package from the Protomatter project [3]
o MAL's mx.Log package [4]
- This proposal will basically look like java.util.logging with a
- smattering of log4j.
-
Simple Example
This shows a very simple example of how the logging package can be
! used to generate simple logging output on stdout.
!
--------- mymodule.py -------------------------------
import logging
log = logging.getLogger("MyModule")
! def doit():
! log.debug("doin' stuff")
! # do stuff ...
-----------------------------------------------------
--------- myapp.py ----------------------------------
import mymodule, logging
log = logging.getLogger("MyApp")
! log.info("start my app")
try:
! mymodule.doit()
except Exception, e:
! log.error("There was a problem doin' stuff.")
! log.info("end my app")
-----------------------------------------------------
! > python myapp.py
! 0 [myapp.py:4] INFO MyApp - start my app
! 36 [mymodule.py:5] DEBUG MyModule - doin' stuff
! 51 [myapp.py:9] INFO MyApp - end my app
! ^^ ^^^^^^^^^^^^ ^^^^ ^^^^^ ^^^^^^^^^^
! | | | | `-- message
! | | | `-- logging name/channel
! | | `-- level
! | `-- location
! `-- time
! NOTE: Not sure exactly what the default format will look like yet.
! Control Flow
! [Note: excerpts from Java Logging Overview. [5]]
Applications make logging calls on *Logger* objects. Loggers are
! organized in a hierarchical namespace and child Loggers may
! inherit some logging properties from their parents in the
! namespace.
!
! Notes on namespace: Logger names fit into a "dotted name"
! namespace, with dots (periods) indicating sub-namespaces. The
! namespace of logger objects therefore corresponds to a single tree
! data structure.
"" is the root of the namespace
--- 45,121 ----
Influences
! This proposal was put together after having studied the
following logging packages:
o java.util.logging in JDK 1.4 (a.k.a. JSR047) [1]
o log4j [2]
o the Syslog package from the Protomatter project [3]
o MAL's mx.Log package [4]
Simple Example
This shows a very simple example of how the logging package can be
! used to generate simple logging output on stderr.
!
--------- mymodule.py -------------------------------
import logging
log = logging.getLogger("MyModule")
! def doIt():
! log.debug("Doin' stuff...")
! #do stuff...
! raise TypeError, "Bogus type error for testing"
-----------------------------------------------------
--------- myapp.py ----------------------------------
import mymodule, logging
+
+ logging.basicConfig()
+
log = logging.getLogger("MyApp")
! log.info("Starting my app")
try:
! mymodule.doIt()
except Exception, e:
! log.exception("There was a problem.")
! log.info("Ending my app")
-----------------------------------------------------
! % python myapp.py
! INFO:MyApp: Starting my app
! DEBUG:MyModule: Doin' stuff...
! ERROR:MyApp: There was a problem.
! Traceback (most recent call last):
! File "myapp.py", line 9, in ?
! mymodule.doIt()
! File "mymodule.py", line 7, in doIt
! raise TypeError, "Bogus type error for testing"
! TypeError: Bogus type error for testing
+ INFO:MyApp: Ending my app
! The above example shows the default output format. All
! aspects of the output format should be configurable, so that
! you could have output formatted like this:
! 2002-04-19 07:56:58,174 MyModule DEBUG - Doin' stuff...
!
! or just
!
! Doin' stuff...
!
!
! Control Flow
Applications make logging calls on *Logger* objects. Loggers are
! organized in a hierarchical namespace and child Loggers inherit
! some logging properties from their parents in the namespace.
!
! Logger names fit into a "dotted name" namespace, with dots
! (periods) indicating sub-namespaces. The namespace of logger
! objects therefore corresponds to a single tree data structure.
"" is the root of the namespace
***************
*** 116,131 ****
"Zope.ZODB" would be a child node of "Zope"
! These Logger objects allocate *LogRecord* objects which are passed
! to *Handler* objects for publication. Both Loggers and Handlers
! may use logging *levels* and (optionally) *Filters* to decide if
! they are interested in a particular LogRecord. When it is
! necessary to publish a LogRecord externally, a Handler can
! (optionally) use a *Formatter* to localize and format the message
! before publishing it to an I/O stream.
Each Logger keeps track of a set of output Handlers. By default
! all Loggers also send their output to their parent Logger. But
! Loggers may also be configured to ignore Handlers higher up the
! tree.
The APIs are structured so that calls on the Logger APIs can be
--- 123,138 ----
"Zope.ZODB" would be a child node of "Zope"
! These Logger objects create *LogRecord* objects which are passed
! to *Handler* objects for output. Both Loggers and Handlers may
! use logging *levels* and (optionally) *Filters* to decide if they
! are interested in a particular LogRecord. When it is necessary to
! output a LogRecord externally, a Handler can (optionally) use a
! *Formatter* to localize and format the message before sending it
! to an I/O stream.
Each Logger keeps track of a set of output Handlers. By default
! all Loggers also send their output to all Handlers of their
! ancestor Loggers. Loggers may, however, also be configured to
! ignore Handlers higher up the tree.
The APIs are structured so that calls on the Logger APIs can be
***************
*** 138,144 ****
Handler requests them.
Levels
!
The logging levels, in increasing order of importance, are:
--- 145,162 ----
Handler requests them.
+ The overall Logger hierarchy can also have a level associated with
+ it, which takes precedence over the levels of individual Loggers.
+ This is done through a module-level function:
+
+ def disable(lvl):
+ """
+ Do not generate any LogRecords for requests with a severity less
+ than 'lvl'.
+ """
+ ...
+
Levels
!
The logging levels, in increasing order of importance, are:
***************
*** 147,160 ****
WARN
ERROR
! FATAL
! ALL
! This is consistent with log4j and Protomatter's Syslog and not
! with JSR047 which has a few more levels and some different names.
! Implementation-wise: these are just integer constants, to allow
! simple comparison of importance. See "What Logging Levels?" below
! for a debate on what standard levels should be defined.
Loggers
--- 165,203 ----
WARN
ERROR
! CRITICAL
! The term CRITICAL is used in preference to FATAL, which is used by
! log4j. The levels are conceptually the same - that of a serious,
! or very serious, error. However, FATAL implies death, which in
! Python implies a raised and uncaught exception, traceback, and
! exit. Since the logging module does not enforce such an outcome
! from a FATAL-level log entry, it makes sense to use CRITICAL in
! preference to FATAL.
! These are just integer constants, to allow simple comparison of
! importance. Experience has shown that too many levels can be
! confusing, as they lead to subjective interpretation of which
! level should be applied to any particular log request.
!
! Although the above levels are strongly recommended, the logging
! system should not be prescriptive. Users may define their own
! levels, as well as the textual representation of any levels. User
! defined levels must, however, obey the constraints that they are
! all positive integers and that they increase in order of
! increasing severity.
!
! User-defined logging levels are supported through two module-level
! functions:
+ def getLevelName(lvl):
+ """Return the text for level 'lvl'."""
+ ...
+
+ def addLevelName(lvl, lvlName):
+ """
+ Add the level 'lvl' with associated text 'levelName', or
+ set the textual representation of existing level 'lvl' to be
+ 'lvlName'."""
+ ...
Loggers
***************
*** 163,195 ****
it is interested in, and discards log requests below that level.
! The *LogManager* maintains a hierarchical namespace of named
! Logger objects. Generations are denoted with dot-separated names:
! Logger "foo" is the parent of Loggers "foo.bar" and "foo.baz".
! The main logging method is:
class Logger:
! def log(self, level, msg, *args):
! """Log 'msg % args' at logging level 'level'."""
...
! however convenience functions are defined for each logging level:
! def debug(self, msg, *args): ...
! def info(self, msg, *args): ...
! def warn(self, msg, *args): ...
! def error(self, msg, *args): ...
! def fatal(self, msg, *args): ...
! XXX How to defined a nice convenience function for logging an exception?
! mx.Log has something like this, doesn't it?
! XXX What about a .raising() convenience function? How about:
! def raising(self, exception, level=ERROR): ...
! It would create a log message describing an exception that is
! about to be raised. I don't like that 'level' is not first
! when it *is* first for .log().
--- 206,329 ----
it is interested in, and discards log requests below that level.
! A *Manager* class instance maintains the hierarchical namespace of
! named Logger objects. Generations are denoted with dot-separated
! names: Logger "foo" is the parent of Loggers "foo.bar" and
! "foo.baz".
! The Manager class instance is a singleton and is not directly
! exposed to users, who interact with it using various module-level
! functions.
!
! The general logging method is:
class Logger:
! def log(self, lvl, msg, *args, **kwargs):
! """Log 'str(msg) % args' at logging level 'lvl'."""
...
! However, convenience functions are defined for each logging level:
! class Logger:
! def debug(self, msg, *args, **kwargs): ...
! def info(self, msg, *args, **kwargs): ...
! def warn(self, msg, *args, **kwargs): ...
! def error(self, msg, *args, **kwargs): ...
! def critical(self, msg, *args, **kwargs): ...
! Only one keyword argument is recognized at present - "exc_info".
! If true, the caller wants exception information to be provided in
! the logging output. This mechanism is only needed if exception
! information needs to be provided at *any* logging level. In the
! more common case, where exception information needs to be added to
! the log only when errors occur, i.e. at the ERROR level, then
! another convenience method is provided:
! class Logger:
! def exception(self, msg, *args): ...
! This should only be called in the context of an exception handler,
! and is the preferred way of indicating a desire for exception
! information in the log. The other convenience methods are
! intended to be called with exc_info only in the unusual situation
! where you might want to provide exception information in the
! context of an INFO message, for example.
! The "msg" argument shown above will normally be a format string;
! however, it can be any object x for which str(x) returns the
! format string. This facilitates, for example, the use of an
! object which fetches a locale- specific message for an
! internationalized/localized application, perhaps using the
! standard gettext module. An outline example:
!
! class Message:
! """Represents a message"""
! def __init__(self, id):
! """Initialize with the message ID"""
!
! def __str__(self):
! """Return an appropriate localized message text"""
!
! ...
!
! logger.info(Message("abc"), ...)
!
! Gathering and formatting data for a log message may be expensive,
! and a waste if the logger was going to discard the message anyway.
! To see if a request will be honoured by the logger, the
! isEnabledFor() method can be used:
!
! class Logger:
! def isEnabledFor(self, lvl):
! """
! Return true if requests at level 'lvl' will NOT be
! discarded.
! """
! ...
!
! so instead of this expensive and possibly wasteful DOM to XML
! conversion:
!
! ...
! hamletStr = hamletDom.toxml()
! log.info(hamletStr)
! ...
!
! one can do this:
!
! if log.isEnabledFor(logging.INFO):
! hamletStr = hamletDom.toxml()
! log.info(hamletStr)
!
! When new loggers are created, they are initialized with a level
! which signifies "no level". A level can be set explicitly using
! the setLevel() method:
!
! class Logger:
! def setLevel(self, lvl): ...
!
! If a logger's level is not set, the system consults all its
! ancestors, walking up the hierarchy until an explicitly set level
! is found. That is regarded as the "effective level" of the
! logger, and can be queried via the getEffectiveLevel() method:
!
! def getEffectiveLevel(self): ...
!
! Loggers are never instantiated directly. Instead, a module-level
! function is used:
!
! def getLogger(name=None): ...
!
! If no name is specified, the root logger is returned. Otherwise,
! if a logger with that name exists, it is returned. If not, a new
! logger is initialized and returned. Here, "name" is synonymous
! with "channel name".
!
! Users can specify a custom subclass of Logger to be used by the
! system when instantiating new loggers:
!
! def setLoggerClass(klass): ...
!
! The passed class should be a subclass of Logger, and it's __init__
! method should call Logger.__init__.
***************
*** 202,217 ****
- FileHandler: A handler for writing to a single file or set
of rotating files.
-
- More standard Handlers may be implemented if deemed desirable and
- feasible. Other interesting candidates:
-
- SocketHandler: A handler for writing to remote TCP ports.
! - CreosoteHandler: A handler for writing to UDP packets, for
low-cost logging. Jeff Bauer already had such a system [5].
- MemoryHandler: A handler that buffers log records in memory
! (JSR047).
! - SMTPHandler: Akin to log4j's SMTPAppender.
! - SyslogHandler: Akin to log4j's SyslogAppender.
! - NTEventLogHandler: Akin to log4j's NTEventLogAppender.
--- 336,386 ----
- FileHandler: A handler for writing to a single file or set
of rotating files.
- SocketHandler: A handler for writing to remote TCP ports.
! - DatagramHandler: A handler for writing to UDP sockets, for
low-cost logging. Jeff Bauer already had such a system [5].
- MemoryHandler: A handler that buffers log records in memory
! until the buffer is full or a particular condition occurs
! [1].
! - SMTPHandler: A handler for sending to email addresses via SMTP.
! - SysLogHandler: A handler for writing to Unix syslog via UDP.
! - NTEventLogHandler: A handler for writing to event logs on
! Windows NT, 2000 and XP.
! - HTTPHandler: A handler for writing to a Web server with
! either GET or POST semantics.
!
! Handlers can also have levels set for them using the
! setLevel() method:
!
! def setLevel(self, lvl): ...
!
!
! The FileHandler can be set up to create a rotating set of log
! files. In this case, the file name passed to the constructor is
! taken as a "base" file name. Additional file names for the
! rotation are created by appending .1, .2, etc. to the base file
! name, up to a maximum as specified when rollover is requested.
! The setRollover method is used to specify a maximum size for a log
! file and a maximum number of backup files in the rotation.
!
! def setRollover(maxBytes, backupCount): ...
!
! If maxBytes is specified as zero, no rollover ever occurs and the
! log file grows indefinitely. If a non-zero size is specified,
! when that size is about to be exceeded, rollover occurs. The
! rollover method ensures that the base file name is always the most
! recent, .1 is the next most recent, .2 the next most recent after
! that, and so on.
!
! There are many additional handlers implemented in the test/example
! scripts provided with [6] - for example, XMLHandler and
! SOAPHandler.
!
!
! LogRecords
!
! A LogRecord acts as a receptacle for information about a
! logging event. It is little more than a dictionary, though it
! does define a getMessage method which merges a message with
! optional runarguments.
***************
*** 222,411 ****
record. The following core Formatters will be implemented:
! - Formatter: Provide printf-like formatting, perhaps akin to
! log4j's PatternAppender.
!
! Other possible candidates for implementation:
! - XMLFormatter: Serialize a LogRecord according to a specific
! schema. Could copy the schema from JSR047's XMLFormatter or
! log4j's XMLAppender.
! - HTMLFormatter: Provide a simple HTML output of log
! information. (See log4j's HTMLAppender.)
! Filters
! A Filter can be called by a Logger or Handler to decide if a
! LogRecord should be logged.
! JSR047 and log4j have slightly different filtering interfaces. The
! former is simpler:
! class Filter:
! def isLoggable(self):
! """Return a boolean."""
! The latter is modeled after Linux's ipchains (where Filter's can
! be chained with each filter either 'DENY'ing, 'ACCEPT'ing, or
! being 'NEUTRAL' on each check). I would probably favor to former
! because it is simpler and I don't immediate see the need for the
! latter.
!
! No filter implementations are currently proposed (other that the
! do nothing base class) because I don't have enough experience to
! know what kinds of filters would be common. Users can always
! subclass Filter for their own purposes. Log4j includes a few
! filters that might be interesting.
! Configuration
! Note: Configuration for the proposed logging system is currently
! under-specified.
! The main benefit of a logging system like this is that one can
! control how much and what logging output one gets from an
! application without changing that application's source code.
! Log4j and Syslog provide for configuration via an external XML
! file. Log4j and JSR047 provide for configuration via Java
! properties (similar to -D #define's to a C/C++ compiler). All
! three provide for configuration via API calls.
! Configuration includes the following:
! - What logging level a logger should be interested in.
! - What handlers should be attached to which loggers.
! - What filters should be attached to which handlers and loggers.
! - Specifying attributes specific to certain Handlers and Filters.
! - Defining the default configuration.
! - XXX Add others.
! In general each application will have its own requirements for how
! a user may configure logging output. One application
! (e.g. distutils) may want to control logging levels via
! '-q,--quiet,-v,--verbose' options to setup.py. Zope may want to
! configure logging via certain environment variables
! (e.g. 'STUPID_LOG_FILE' :). Komodo may want to configure logging
! via its preferences system.
! This PEP proposes to clearly document the API for configuring each
! of the above listed configurable elements and to define a
! reasonable default configuration. This PEP does not propose to
! define a general XML or .ini file configuration schema and the
! backend to parse it.
!
! It might, however, be worthwhile to define an abstraction of the
! configuration API to allow the expressiveness of Syslog
! configuration. Greg Wilson made this argument:
! In Protomatter [Syslog], you configure by saying "give me
! everything that matches these channel+level combinations",
! such as "server.error" and "database.*". The log4j "configure
! by inheritance" model, on the other hand, is very clever, but
! hard for non-programmers to manage without a GUI that
! essentially reduces it to Protomatter's.
! Case Scenarios
- This section presents a few usage scenarios which will be used to
- help decide how best to specify the logging API.
! (1) A short simple script.
! This script does not have many lines. It does not heavily use
! any third party modules (i.e. the only code doing any logging
! would be the main script). Only one logging channel is really
! needed and thus, the channel name is unnecessary. The user
! doesn't want to bother with logging system configuration much.
! (2) Medium sized app with C extension module.
!
! Includes a few Python modules and a main script. Employs,
! perhaps, a few logging channels. Includes a C extension
! module which might want to make logging calls as well.
! (3) Distutils.
! A large number of Python packages/modules. Perhaps (but not
! necessarily) a number of logging channels are used.
! Specifically needs to facilitate the controlling verbosity
! levels via simple command line options to 'setup.py'.
- (4) Large, possibly multi-language, app. E.g. Zope or (my
- experience) Komodo.
! (I don't expect this logging system to deal with any
! cross-language issues but it is something to think about.)
! Many channels are used. Many developers involved. People
! providing user support are possibly not the same people who
! developed the application. Users should be able to generate
! log files (i.e. configure logging) while reproducing a bug to
! send back to developers.
! Implementation
! XXX Details to follow consensus that this proposal is a good idea.
! What Logging Levels?
! The following are the logging levels defined by the systems I looked at:
! - log4j: DEBUG, INFO, WARN, ERROR, FATAL
! - syslog: DEBUG, INFO, WARNING, ERROR, FATAL
! - JSR047: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE
! - zLOG (used by Zope):
! TRACE=-300 -- Trace messages
! DEBUG=-200 -- Debugging messages
! BLATHER=-100 -- Somebody shut this app up.
! INFO=0 -- For things like startup and shutdown.
! PROBLEM=100 -- This isn't causing any immediate problems, but
! deserves attention.
! WARNING=100 -- A wishy-washy alias for PROBLEM.
! ERROR=200 -- This is going to have adverse effects.
! PANIC=300 -- We're dead!
! - mx.Log:
! SYSTEM_DEBUG
! SYSTEM_INFO
! SYSTEM_UNIMPORTANT
! SYSTEM_MESSAGE
! SYSTEM_WARNING
! SYSTEM_IMPORTANT
! SYSTEM_CANCEL
! SYSTEM_ERROR
! SYSTEM_PANIC
! SYSTEM_FATAL
! The current proposal is to copy log4j. XXX I suppose I could see
! adding zLOG's "TRACE" level, but I am not sure of the usefulness
! of others.
- Static Logging Methods (as per Syslog)?
! Both zLOG and Syslog provide module-level logging functions rather
! (or in addition to) logging methods on a created Logger object.
! XXX Is this something that is deemed worth including?
! Pros:
! - It would make the simplest case shorter:
- import logging
- logging.error("Something is wrong")
! instead of
! import logging
! log = logging.getLogger("")
! log.error("Something is wrong")
! Cons:
! - It provides more than one way to do it.
! - It encourages logging without a channel name, because this
! mechanism would likely be implemented by implicitly logging
! on the root (and nameless) logger of the hierarchy.
--- 391,576 ----
record. The following core Formatters will be implemented:
! - Formatter: Provide printf-like formatting, using the % operator.
! - BufferingFormatter: Provide formatting for multiple
! messages, with header and trailer formatting support.
+ Formatters are associated with Handlers by calling setFormatter()
+ on a handler:
! def setFormatter(self, form): ...
! Formatters use the % operator to format the logging message. The
! format string should contain %(name)x and the attribute dictionary
! of the LogRecord is used to obtain message-specific data. The
! following attributes are provided:
! %(name)s Name of the logger (logging channel)
! %(levelno)s Numeric logging level for the message (DEBUG,
! INFO, WARN, ERROR, CRITICAL)
! %(levelname)s Text logging level for the message ("DEBUG", "INFO",
! "WARN", "ERROR", "CRITICAL")
+ %(pathname)s Full pathname of the source file where the logging
+ call was issued (if available)
! %(filename)s Filename portion of pathname
! %(module)s Module from which logging call was made
! %(lineno)d Source line number where the logging call was issued
! (if available)
! %(created)f Time when the LogRecord was created (time.time()
! return value)
! %(asctime)s Textual time when the LogRecord was created
! %(msecs)d Millisecond portion of the creation time
! %(relativeCreated)d Time in milliseconds when the LogRecord was created,
! relative to the time the logging module was loaded
! (typically at application startup time)
! %(thread)d Thread ID (if available)
! %(message)s The result of record.getMessage(), computed just as
! the record is emitted
+ If a formatter sees that the format string includes "(asctime)s",
+ the creation time is formatted into the LogRecord's asctime
+ attribute. To allow flexibility in formatting dates, Formatters
+ are initialized with a format string for the message as a whole,
+ and a separate format string for date/time. The date/time format
+ string should be in time.strftime format. The default value for
+ the message format is "%(message)s". The default date/time format
+ is ISO8601.
! The formatter uses a class attribute, "converter", to indicate how
! to convert a time from seconds to a tuple. By default, the value
! of "converter" is "time.localtime". If needed, a different
! converter (e.g. "time.gmtime") can be set on an individual
! formatter instance, or the class attribute changed to affect all
! formatter instances.
! Filters
! When level-based filtering is insufficient, a Filter can be called
! by a Logger or Handler to decide if a LogRecord should be output.
! Loggers and Handlers can have multiple filters installed, and any
! one of them can veto a LogRecord being output.
! class Filter:
! def filter(self, record):
! """
! Return a value indicating true if the record is to be
! processed. Possibly modify the record, if deemed
! appropriate by the filter.
! """
! The default behaviour allows a Filter to be initialized with a
! Logger name. This will only allow through events which are
! generated using the named logger or any of its children. For
! example, a filter initialized with "A.B" will allow events logged
! by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB",
! "B.A.B" etc. If initialized with the empty string, all events are
! passed by the Filter. This filter behaviour is useful when it is
! desired to focus attention on one particular area of an
! application; the focus can be changed simply by changing a filter
! attached to the root logger.
! There are many examples of Filters provided in [6].
! Configuration
+ The main benefit of a logging system like this is that one can
+ control how much and what logging output one gets from an
+ application without changing that application's source code.
+ Therefore, although configuration can be performed through the
+ logging API, it must also be possible to change the logging
+ configuration without changing an application at all. For
+ long-running programs like Zope, it should be possible to change
+ the logging configuration while the program is running.
! Configuration includes the following:
! - What logging level a logger or handler should be interested in.
! - What handlers should be attached to which loggers.
! - What filters should be attached to which handlers and loggers.
! - Specifying attributes specific to certain handlers and filters.
+ In general each application will have its own requirements for how
+ a user may configure logging output. However, each application
+ will specify the required configuration to the logging system
+ through a standard mechanism.
! The most simple configuration is that of a single handler, writing
! to stderr, attached to the root logger. This configuration is set
! up by calling the basicConfig() function once the logging module
! has been imported.
! def basicConfig(): ...
! For more sophisticated configurations, this PEP makes no specific
! proposals, for the following reasons:
! - A specific proposal may be seen as prescriptive.
! - Without the benefit of wide practical experience in the
! Python community, there is no way to know whether any given
! configuration approach is a good one. That practice can't
! really come until the logging module is used, and that means
! until *after* Python 2.3 has shipped.
! - There is a likelihood that different types of applications
! may require different configuration approaches, so that no
! "one size fits all".
+ The reference implementation [6] has a working configuration file
+ format, implemented for the purpose of proving the concept and
+ suggesting one possible alternative. It may be that separate
+ extension modules, not part of the core Python distribution, are
+ created for logging configuration and log viewing, supplemental
+ handlers and other features which are not of interest to the bulk
+ of the community.
! Thread Safety
! The logging system should support thread-safe operation without
! any special action needing to be taken by its users.
! Module-Level Functions
! To support use of the logging mechanism in short scripts and small
! applications, module-level functions debug(), info(), warn(),
! error(), critical() and exception() are provided. These work in
! the same way as the correspondingly named methods of Logger - in
! fact they delegate to the corresponding methods on the root
! logger. A further convenience provided by these functions is that
! if no configuration has been done, basicConfig() is automatically
! called.
! At application exit, all handlers can be flushed by calling the function
!
! def shutdown(): ...
!
! This will flush and close all handlers.
!
!
! Implementation
!
! The reference implementation is Vinay Sajip's logging module [6].
!
!
! Packaging
!
! The reference implementation is implemented as a single module.
! This offers the simplest interface - all users have to do is
! "import logging" and they are in a position to use all the
! functionality available.
***************
*** 423,431 ****
[4] MAL mentions his mx.Log logging module:
! http://mail.python.org/pipermail/python-dev/2002-February/019767.html
[5] Jeff Bauer's Mr. Creosote
http://starship.python.net/crew/jbauer/creosote/
Copyright
--- 588,600 ----
[4] MAL mentions his mx.Log logging module:
! http://mail.python.org/pipermail/python-dev/2002-February/019767.html
[5] Jeff Bauer's Mr. Creosote
http://starship.python.net/crew/jbauer/creosote/
+ [6] Vinay Sajip's logging module.
+ http://www.red-dove.com/python_logging.html
+
+
Copyright
***************
*** 437,440 ****
--- 606,610 ----
mode: indented-text
indent-tabs-mode: nil
+ sentence-end-double-space: t
fill-column: 70
End: