Updated information on logging contextual information.

This commit is contained in:
Vinay Sajip 2010-09-06 22:18:20 +00:00
parent 912443c861
commit 496153952a
1 changed files with 283 additions and 20 deletions

View File

@ -246,16 +246,16 @@ With the logger object configured, the following methods create log messages:
methods listed above, but this is how to log at custom log levels. methods listed above, but this is how to log at custom log levels.
:func:`getLogger` returns a reference to a logger instance with the specified :func:`getLogger` returns a reference to a logger instance with the specified
if it it is provided, or ``root`` if not. The names are period-separated name if it is provided, or ``root`` if not. The names are period-separated
hierarchical structures. Multiple calls to :func:`getLogger` with the same name hierarchical structures. Multiple calls to :func:`getLogger` with the same name
will return a reference to the same logger object. Loggers that are further will return a reference to the same logger object. Loggers that are further
down in the hierarchical list are children of loggers higher up in the list. down in the hierarchical list are children of loggers higher up in the list.
For example, given a logger with a name of ``foo``, loggers with names of For example, given a logger with a name of ``foo``, loggers with names of
``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all children of ``foo``. ``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all descendants of ``foo``.
Child loggers propagate messages up to their parent loggers. Because of this, Child loggers propagate messages up to the handlers associated with their
it is unnecessary to define and configure all the loggers an application uses. ancestor loggers. Because of this, it is unnecessary to define and configure
It is sufficient to configure a top-level logger and create child loggers as handlers for all the loggers an application uses. It is sufficient to
needed. configure handlers for a top-level logger and create child loggers as needed.
Handlers Handlers
@ -283,15 +283,16 @@ custom handlers) are the following configuration methods:
are there two :func:`setLevel` methods? The level set in the logger are there two :func:`setLevel` methods? The level set in the logger
determines which severity of messages it will pass to its handlers. The level determines which severity of messages it will pass to its handlers. The level
set in each handler determines which messages that handler will send on. set in each handler determines which messages that handler will send on.
:func:`setFormatter` selects a Formatter object for this handler to use.
* :func:`setFormatter` selects a Formatter object for this handler to use.
* :func:`addFilter` and :func:`removeFilter` respectively configure and * :func:`addFilter` and :func:`removeFilter` respectively configure and
deconfigure filter objects on handlers. deconfigure filter objects on handlers.
Application code should not directly instantiate and use handlers. Instead, the Application code should not directly instantiate and use instances of
:class:`Handler` class is a base class that defines the interface that all :class:`Handler`. Instead, the :class:`Handler` class is a base class that
Handlers should have and establishes some default behavior that child classes defines the interface that all handlers should have and establishes some
can use (or override). default behavior that child classes can use (or override).
Formatters Formatters
@ -318,6 +319,14 @@ order::
"%(asctime)s - %(levelname)s - %(message)s" "%(asctime)s - %(levelname)s - %(message)s"
Formatters use a user-configurable function to convert the creation time of a
record to a tuple. By default, :func:`time.localtime` is used; to change this
for a particular formatter instance, set the ``converter`` attribute of the
instance to a function with the same signature as :func:`time.localtime` or
:func:`time.gmtime`. To change it for all formatters, for example if you want
all logging times to be shown in GMT, set the ``converter`` attribute in the
Formatter class (to ``time.gmtime`` for GMT display).
Configuring Logging Configuring Logging
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
@ -464,9 +473,8 @@ libraries, then the logger name specified can be "orgname.foo" rather than
just "foo". just "foo".
.. versionadded:: 3.1 .. versionadded:: 3.1
The :class:`NullHandler` class was not present in previous versions, but is
The :class:`NullHandler` class was not present in previous versions, but is now now included, so that it need not be defined in library code.
included, so that it need not be defined in library code.
@ -516,7 +524,9 @@ support desk staff, system administrators, developers). Handlers are passed
can have zero, one or more handlers associated with it (via the can have zero, one or more handlers associated with it (via the
:meth:`addHandler` method of :class:`Logger`). In addition to any handlers :meth:`addHandler` method of :class:`Logger`). In addition to any handlers
directly associated with a logger, *all handlers associated with all ancestors directly associated with a logger, *all handlers associated with all ancestors
of the logger* are called to dispatch the message. of the logger* are called to dispatch the message (unless the *propagate* flag
for a logger is set to a false value, at which point the passing to ancestor
handlers stops).
Just as for loggers, handlers can have levels associated with them. A handler's Just as for loggers, handlers can have levels associated with them. A handler's
level acts as a filter in the same way as a logger's level does. If a handler level acts as a filter in the same way as a logger's level does. If a handler
@ -729,7 +739,11 @@ functions.
Provides an overriding level *lvl* for all loggers which takes precedence over Provides an overriding level *lvl* for all loggers which takes precedence over
the logger's own level. When the need arises to temporarily throttle logging the logger's own level. When the need arises to temporarily throttle logging
output down across the whole application, this function can be useful. output down across the whole application, this function can be useful. Its
effect is to disable all logging calls of severity *lvl* and below, so that
if you call it with a value of INFO, then all INFO and DEBUG events would be
discarded, whereas those of severity WARNING and above would be processed
according to the logger's effective level.
.. function:: addLevelName(lvl, levelName) .. function:: addLevelName(lvl, levelName)
@ -765,11 +779,13 @@ functions.
Does basic configuration for the logging system by creating a Does basic configuration for the logging system by creating a
:class:`StreamHandler` with a default :class:`Formatter` and adding it to the :class:`StreamHandler` with a default :class:`Formatter` and adding it to the
root logger. The function does nothing if any handlers have been defined for root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
the root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
:func:`error` and :func:`critical` will call :func:`basicConfig` automatically :func:`error` and :func:`critical` will call :func:`basicConfig` automatically
if no handlers are defined for the root logger. if no handlers are defined for the root logger.
This function does nothing if the root logger already has handlers
configured for it.
The following keyword arguments are supported. The following keyword arguments are supported.
+--------------+---------------------------------------------+ +--------------+---------------------------------------------+
@ -838,8 +854,8 @@ instantiated directly, but always through the module-level function
.. attribute:: Logger.propagate .. attribute:: Logger.propagate
If this evaluates to false, logging messages are not passed by this logger or by If this evaluates to false, logging messages are not passed by this logger or by
child loggers to higher level (ancestor) loggers. The constructor sets this its child loggers to the handlers of higher level (ancestor) loggers. The
attribute to 1. constructor sets this attribute to 1.
.. method:: Logger.setLevel(lvl) .. method:: Logger.setLevel(lvl)
@ -1195,6 +1211,28 @@ are sent to both destinations.
This example uses console and file handlers, but you can use any number and This example uses console and file handlers, but you can use any number and
combination of handlers you choose. combination of handlers you choose.
.. _logging-exceptions:
Exceptions raised during logging
--------------------------------
The logging package is designed to swallow exceptions which occur while logging
in production. This is so that errors which occur while handling logging events
- such as logging misconfiguration, network or other similar errors - do not
cause the application using logging to terminate prematurely.
:class:`SystemExit` and :class:`KeyboardInterrupt` exceptions are never
swallowed. Other exceptions which occur during the :meth:`emit` method of a
:class:`Handler` subclass are passed to its :meth:`handleError` method.
The default implementation of :meth:`handleError` in :class:`Handler` checks
to see if a module-level variable, :data:`raiseExceptions`, is set. If set, a
traceback is printed to :data:`sys.stderr`. If not set, the exception is swallowed.
**Note:** The default value of :data:`raiseExceptions` is ``True``. This is because
during development, you typically want to be notified of any exceptions that
occur. It's advised that you set :data:`raiseExceptions` to ``False`` for production
usage.
.. _context-info: .. _context-info:
@ -1214,6 +1252,10 @@ level of granularity you want to use in logging an application, it could
be hard to manage if the number of :class:`Logger` instances becomes be hard to manage if the number of :class:`Logger` instances becomes
effectively unbounded. effectively unbounded.
Using LoggerAdapters to impart contextual information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
An easy way in which you can pass contextual information to be output along An easy way in which you can pass contextual information to be output along
with logging event information is to use the :class:`LoggerAdapter` class. with logging event information is to use the :class:`LoggerAdapter` class.
This class is designed to look like a :class:`Logger`, so that you can call This class is designed to look like a :class:`Logger`, so that you can call
@ -1318,6 +1360,104 @@ When this script is run, the output should look something like this::
2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters 2008-01-18 14:49:54,033 d.e.f WARNING IP: 127.0.0.1 User: jim A message at WARNING level with 2 parameters
Using Filters to impart contextual information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can also add contextual information to log output using a user-defined
:class:`Filter`. ``Filter`` instances are allowed to modify the ``LogRecords``
passed to them, including adding additional attributes which can then be output
using a suitable format string, or if needed a custom :class:`Formatter`.
For example in a web application, the request being processed (or at least,
the interesting parts of it) can be stored in a threadlocal
(:class:`threading.local`) variable, and then accessed from a ``Filter`` to
add, say, information from the request - say, the remote IP address and remote
user's username - to the ``LogRecord``, using the attribute names 'ip' and
'user' as in the ``LoggerAdapter`` example above. In that case, the same format
string can be used to get similar output to that shown above. Here's an example
script::
import logging
from random import choice
class ContextFilter(logging.Filter):
"""
This is a filter which injects contextual information into the log.
Rather than use actual contextual information, we just use random
data in this demo.
"""
USERS = ['jim', 'fred', 'sheila']
IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']
def filter(self, record):
record.ip = choice(ContextFilter.IPS)
record.user = choice(ContextFilter.USERS)
return True
if __name__ == "__main__":
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
a1 = logging.LoggerAdapter(logging.getLogger("a.b.c"),
{ "ip" : "123.231.231.123", "user" : "sheila" })
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s")
a1 = logging.getLogger("a.b.c")
a2 = logging.getLogger("d.e.f")
f = ContextFilter()
a1.addFilter(f)
a2.addFilter(f)
a1.debug("A debug message")
a1.info("An info message with %s", "some parameters")
for x in range(10):
lvl = choice(levels)
lvlname = logging.getLevelName(lvl)
a2.log(lvl, "A message at %s level with %d %s", lvlname, 2, "parameters")
which, when run, produces something like::
2010-09-06 22:38:15,292 a.b.c DEBUG IP: 123.231.231.123 User: fred A debug message
2010-09-06 22:38:15,300 a.b.c INFO IP: 192.168.0.1 User: sheila An info message with some parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR IP: 127.0.0.1 User: jim A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG IP: 127.0.0.1 User: sheila A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,300 d.e.f ERROR IP: 123.231.231.123 User: fred A message at ERROR level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f CRITICAL IP: 127.0.0.1 User: sheila A message at CRITICAL level with 2 parameters
2010-09-06 22:38:15,300 d.e.f DEBUG IP: 192.168.0.1 User: jim A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters
2010-09-06 22:38:15,301 d.e.f DEBUG IP: 123.231.231.123 User: fred A message at DEBUG level with 2 parameters
2010-09-06 22:38:15,301 d.e.f INFO IP: 123.231.231.123 User: fred A message at INFO level with 2 parameters
.. _multiple-processes:
Logging to a single file from multiple processes
------------------------------------------------
Although logging is thread-safe, and logging to a single file from multiple
threads in a single process *is* supported, logging to a single file from
*multiple processes* is *not* supported, because there is no standard way to
serialize access to a single file across multiple processes in Python. If you
need to log to a single file from multiple processes, the best way of doing
this is to have all the processes log to a :class:`SocketHandler`, and have a
separate process which implements a socket server which reads from the socket
and logs to file. (If you prefer, you can dedicate one thread in one of the
existing processes to perform this function.) The following section documents
this approach in more detail and includes a working socket receiver which can
be used as a starting point for you to adapt in your own applications.
If you are using a recent version of Python which includes the
:mod:`multiprocessing` module, you can write your own handler which uses the
:class:`Lock` class from this module to serialize access to the file from
your processes. The existing :class:`FileHandler` and subclasses do not make
use of :mod:`multiprocessing` at present, though they may do so in the future.
Note that at present, the :mod:`multiprocessing` module does not provide
working lock functionality on all platforms (see
http://bugs.python.org/issue3770).
.. _network-logging: .. _network-logging:
@ -1449,6 +1589,11 @@ printed on the console; on the server side, you should see something like::
69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack. 69 myapp.area2 WARNING Jail zesty vixen who grabbed pay from quack.
69 myapp.area2 ERROR The five boxing wizards jump quickly. 69 myapp.area2 ERROR The five boxing wizards jump quickly.
Note that there are some security issues with pickle in some scenarios. If
these affect you, you can use an alternative serialization scheme by overriding
the :meth:`makePickle` method and implementing your alternative there, as
well as adapting the above script to use your alternative serialization.
Using arbitrary objects as messages Using arbitrary objects as messages
----------------------------------- -----------------------------------
@ -1604,6 +1749,8 @@ subclasses. However, the :meth:`__init__` method in subclasses needs to call
:exc:`NotImplementedError`. :exc:`NotImplementedError`.
.. _stream-handler:
StreamHandler StreamHandler
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@ -1637,6 +1784,8 @@ and :meth:`flush` methods).
no output, so an explicit :meth:`flush` call may be needed at times. no output, so an explicit :meth:`flush` call may be needed at times.
.. _file-handler:
FileHandler FileHandler
^^^^^^^^^^^ ^^^^^^^^^^^
@ -1663,6 +1812,7 @@ sends logging output to a disk file. It inherits the output functionality from
Outputs the record to the file. Outputs the record to the file.
.. _null-handler:
NullHandler NullHandler
^^^^^^^^^^^ ^^^^^^^^^^^
@ -1686,6 +1836,8 @@ for use by library developers.
See :ref:`library-config` for more information on how to use See :ref:`library-config` for more information on how to use
:class:`NullHandler`. :class:`NullHandler`.
.. _watched-file-handler:
WatchedFileHandler WatchedFileHandler
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
@ -1724,6 +1876,7 @@ this value.
changed. If it has, the existing stream is flushed and closed and the changed. If it has, the existing stream is flushed and closed and the
file opened again, before outputting the record to the file. file opened again, before outputting the record to the file.
.. _rotating-file-handler:
RotatingFileHandler RotatingFileHandler
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
@ -1764,6 +1917,7 @@ module, supports rotation of disk log files.
Outputs the record to the file, catering for rollover as described Outputs the record to the file, catering for rollover as described
previously. previously.
.. _timed-rotating-file-handler:
TimedRotatingFileHandler TimedRotatingFileHandler
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
@ -1811,6 +1965,9 @@ timed intervals.
one is deleted. The deletion logic uses the interval to determine which one is deleted. The deletion logic uses the interval to determine which
files to delete, so changing the interval may leave old files lying around. files to delete, so changing the interval may leave old files lying around.
If *delay* is true, then file opening is deferred until the first call to
:meth:`emit`.
.. method:: doRollover() .. method:: doRollover()
@ -1822,6 +1979,8 @@ timed intervals.
Outputs the record to the file, catering for rollover as described above. Outputs the record to the file, catering for rollover as described above.
.. _socket-handler:
SocketHandler SocketHandler
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@ -1868,6 +2027,11 @@ sends logging output to a network socket. The base class uses a TCP socket.
Pickles the record's attribute dictionary in binary format with a length Pickles the record's attribute dictionary in binary format with a length
prefix, and returns it ready for transmission across the socket. prefix, and returns it ready for transmission across the socket.
Note that pickles aren't completely secure. If you are concerned about
security, you may want to override this method to implement a more secure
mechanism. For example, you can sign pickles using HMAC and then verify
them on the receiving end, or alternatively you can disable unpickling of
global objects on the receiving end.
.. method:: send(packet) .. method:: send(packet)
@ -1875,6 +2039,8 @@ sends logging output to a network socket. The base class uses a TCP socket.
partial sends which can happen when the network is busy. partial sends which can happen when the network is busy.
.. _datagram-handler:
DatagramHandler DatagramHandler
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
@ -1908,6 +2074,8 @@ over UDP sockets.
Send a pickled string to a socket. Send a pickled string to a socket.
.. _syslog-handler:
SysLogHandler SysLogHandler
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@ -1944,6 +2112,87 @@ supports sending logging messages to a remote or local Unix syslog.
or integers - if strings are passed, internal mapping dictionaries are or integers - if strings are passed, internal mapping dictionaries are
used to convert them to integers. used to convert them to integers.
The symbolic ``LOG_`` values are defined in :class:`SysLogHandler` and
mirror the values defined in the ``sys/syslog.h`` header file.
**Priorities**
+--------------------------+---------------+
| Name (string) | Symbolic value|
+==========================+===============+
| ``alert`` | LOG_ALERT |
+--------------------------+---------------+
| ``crit`` or ``critical`` | LOG_CRIT |
+--------------------------+---------------+
| ``debug`` | LOG_DEBUG |
+--------------------------+---------------+
| ``emerg`` or ``panic`` | LOG_EMERG |
+--------------------------+---------------+
| ``err`` or ``error`` | LOG_ERR |
+--------------------------+---------------+
| ``info`` | LOG_INFO |
+--------------------------+---------------+
| ``notice`` | LOG_NOTICE |
+--------------------------+---------------+
| ``warn`` or ``warning`` | LOG_WARNING |
+--------------------------+---------------+
**Facilities**
+---------------+---------------+
| Name (string) | Symbolic value|
+===============+===============+
| ``auth`` | LOG_AUTH |
+---------------+---------------+
| ``authpriv`` | LOG_AUTHPRIV |
+---------------+---------------+
| ``cron`` | LOG_CRON |
+---------------+---------------+
| ``daemon`` | LOG_DAEMON |
+---------------+---------------+
| ``ftp`` | LOG_FTP |
+---------------+---------------+
| ``kern`` | LOG_KERN |
+---------------+---------------+
| ``lpr`` | LOG_LPR |
+---------------+---------------+
| ``mail`` | LOG_MAIL |
+---------------+---------------+
| ``news`` | LOG_NEWS |
+---------------+---------------+
| ``syslog`` | LOG_SYSLOG |
+---------------+---------------+
| ``user`` | LOG_USER |
+---------------+---------------+
| ``uucp`` | LOG_UUCP |
+---------------+---------------+
| ``local0`` | LOG_LOCAL0 |
+---------------+---------------+
| ``local1`` | LOG_LOCAL1 |
+---------------+---------------+
| ``local2`` | LOG_LOCAL2 |
+---------------+---------------+
| ``local3`` | LOG_LOCAL3 |
+---------------+---------------+
| ``local4`` | LOG_LOCAL4 |
+---------------+---------------+
| ``local5`` | LOG_LOCAL5 |
+---------------+---------------+
| ``local6`` | LOG_LOCAL6 |
+---------------+---------------+
| ``local7`` | LOG_LOCAL7 |
+---------------+---------------+
.. method:: mapPriority(levelname)
Maps a logging level name to a syslog priority name.
You may need to override this if you are using custom levels, or
if the default algorithm is not suitable for your needs. The
default algorithm maps ``DEBUG``, ``INFO``, ``WARNING``, ``ERROR`` and
``CRITICAL`` to the equivalent syslog names, and all other level
names to "warning".
.. _nt-eventlog-handler:
NTEventLogHandler NTEventLogHandler
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
@ -2010,6 +2259,7 @@ extensions for Python installed.
lookup to get the message ID. This version returns 1, which is the base lookup to get the message ID. This version returns 1, which is the base
message ID in :file:`win32service.pyd`. message ID in :file:`win32service.pyd`.
.. _smtp-handler:
SMTPHandler SMTPHandler
^^^^^^^^^^^ ^^^^^^^^^^^
@ -2038,6 +2288,7 @@ supports sending logging messages to an email address via SMTP.
If you want to specify a subject line which is record-dependent, override If you want to specify a subject line which is record-dependent, override
this method. this method.
.. _memory-handler:
MemoryHandler MemoryHandler
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
@ -2108,6 +2359,8 @@ should, then :meth:`flush` is expected to do the needful.
Checks for buffer full or a record at the *flushLevel* or higher. Checks for buffer full or a record at the *flushLevel* or higher.
.. _http-handler:
HTTPHandler HTTPHandler
^^^^^^^^^^^ ^^^^^^^^^^^
@ -2250,6 +2503,7 @@ Currently, the useful mapping keys in a :class:`LogRecord` are:
just uses :func:`traceback.print_exception`. The resulting string is just uses :func:`traceback.print_exception`. The resulting string is
returned. returned.
.. _filter:
Filter Objects Filter Objects
-------------- --------------
@ -2275,6 +2529,14 @@ initialized with the empty string, all events are passed.
yes. If deemed appropriate, the record may be modified in-place by this yes. If deemed appropriate, the record may be modified in-place by this
method. method.
Note that filters attached to handlers are consulted whenever an event is
emitted by the handler, whereas filters attached to loggers are consulted
whenever an event is logged to the handler (using :meth:`debug`, :meth:`info`,
etc.) This means that events which have been generated by descendant loggers
will not be filtered by a logger's filter setting, unless the filter has also
been applied to those descendant loggers.
.. _log-record:
LogRecord Objects LogRecord Objects
----------------- -----------------
@ -2306,6 +2568,7 @@ made, and any exception information to be logged.
Returns the message for this :class:`LogRecord` instance after merging any Returns the message for this :class:`LogRecord` instance after merging any
user-supplied arguments with the message. user-supplied arguments with the message.
.. _logger-adapter:
LoggerAdapter Objects LoggerAdapter Objects
--------------------- ---------------------