Zope 3 Logging

 Logging is done through the logging package (PEP 282-based logging).
 It is fairly easy to use and to configure.


 Configuring the logging package for z3.py

  XXX This is out of date; see zope.conf.in for an example of the
  current way of configuring logging.

  The log file used by z3.py can be configured in the zserver.zcml
  file with this directive::

    <startup:useLog file="..." level="...">

  The file argument should give the filename where you want the log to
  go.  If the filename is STDERR or STDOUT, logging goes to the
  process's standard error or standard output stream, respectively.

  The level argument should give the logging severity level; anything
  below this level is not logged.  The supported levels are, in
  increasing severity: DEBUG, INFO, WARN, ERROR, CRITICAL.


 Configuring the logging package for running unit tests

  When running unit tests, logging is configured through the file
  log.ini in the current directory, if it exists.  This file should be
  self-explanatory, and provides full access to the logging package's
  advanced features.  If log.ini is not found, critical messages are
  logged to the process's standard error stream, and other messages
  are not logged.

  test.py also understands the LOGGING environment variable, which can
  be used to specify an integer logging level.  So a simple way to run
  the tests with all logging going to standard error is to set LOGGING
  to 0, e.g.:

  export LOGGING=0
  python test.py


 Using the logging package

  There are two ways of using the logging package.  You can use
  functions defined in the logging package directly, or you can use
  methods on a logger object.  In either case you need a simple import
  statement::

    import logging


 Using the logging functions

  To use the logging functions defined by the package directly, use
  one of the functions debug(), info(), warn(), error() or critical()
  from the package.  Each of these takes a message argument.  The
  message may be a standard Python format string, and then the
  following arguments are the format arguments.  This allows you to
  write, for example::

    logging.warn("Cannot open file %r: %s", filename, err)

  instead of::

    logging.warn("Cannot open file %r: %s" % (filename, err))

  Apart from slight savings in typing, the advantage of the former is
  that if warnings are not logged, the string formatting operation is
  not carried out, saving some time.

  It is also possible to log a traceback.  This is done by adding a
  keyword argument exc_info=True.  For example::

    try:
        ...something...
    except:
        logging.error("Unexpected problem", exc_info=True)

  The logging package will call sys.exc_info() and use the traceback
  module to format the traceback.  When the message is not logged,
  this is skipped.  In fact, there's a shorthand for this particular
  case (logging a traceback at the error level)::

    try:
        ...something...
    except:
        logging.exception("Unexpected problem")

  Finally, there is a generic log function; it has a first argument
  specifying a logging severity level, followed by the standard
  arguments of all the above functions::

    logging.log(level, message, ..., exc_info=...)

  The predefined logging levels are available as symbolic constants:
  logging.DEBUG, logging.INFO, logging.WARN, logging.ERROR, and
  logging.CRITICAL.  (There's no logging.EXCEPTION level, because
  exception() is not a separate logging level; it's a shorthand for
  passing exc_info=True to the error() method.)


 Using a logger object

  Often you'd like all log messages coming out of a particular class
  or module to be "tagged" with a label identifying that class or
  module, regardless of the logging severity of the message.  In some
  cases, you'd like that label to convey additional run-time
  information, such as a storage or thread name.

  Rather than prefixing all log messages with an identifying string,
  you can create a logger object that does this for you.  Logger
  objects have methods debug(), info(), etc., corresponding to the
  logging functions described in the previous section, and with
  exactly the same signature; these are what you use to log a message
  using a logger object, for example::

    logger.warn("Oil temperature: %g", temp)

  To create a logger object, use the getLogger() function::

    foo_bar_logger = logging.getLogger("foo.bar")

  The string argument to getLogger() is interpreted as a sequence of
  names separated by dots; this creates a hierarchy that can be used
  for additional filtering or handling.  Normally, however, a logger
  object inherits all its properties (except for its name) from its
  parent logger object.  For the foo_bar_logger above, the parent
  would be the logger object returned by this call::

    foo_logger = logging.getLogger("foo")

  Its parent in turn is the root logger; the logging functions
  described in the previous section correspond to methods of the root
  logger object.  By configuring the root logger you can configure all
  loggers in the hierarchy, unless some configuration is overridden at
  a lower level.  This an advanced feature of the logging module that
  we won't discuss further here.

  Logger objects are lightweight and cached by the logging module;
  subsequent calls to logging.getLogger() with the same logger name
  will return the same logger object.  However, there is no way to
  delete logger objects, so it's not a good idea to make up arbitrary
  logger names dynamically.
