==============
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.

Only positional format arguments are supported.

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.
