diff options
author | Christian Heimes <christian@cheimes.de> | 2008-01-18 18:40:46 (GMT) |
---|---|---|
committer | Christian Heimes <christian@cheimes.de> | 2008-01-18 18:40:46 (GMT) |
commit | 04c420f605b2bb7902f1afc3d62a45fbb0295bd0 (patch) | |
tree | 1f020f11a29cf7646f22275583fb6a9be1a8925c /Doc/library | |
parent | 679db4aa99352abc3c9d93dcfc30e772760a43da (diff) | |
download | cpython-04c420f605b2bb7902f1afc3d62a45fbb0295bd0.zip cpython-04c420f605b2bb7902f1afc3d62a45fbb0295bd0.tar.gz cpython-04c420f605b2bb7902f1afc3d62a45fbb0295bd0.tar.bz2 |
Merged revisions 60043-60052 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r60043 | christian.heimes | 2008-01-18 10:51:43 +0100 (Fri, 18 Jan 2008) | 2 lines
Build _ctypes after the other extensions. Its build process depends
on the _weakref extension (and maybe other modules, too)
........
r60048 | christian.heimes | 2008-01-18 12:58:50 +0100 (Fri, 18 Jan 2008) | 2 lines
Added win_add2path.py to Tools/scripts/
Added builddoc.bat to Doc/
........
r60049 | vinay.sajip | 2008-01-18 16:54:14 +0100 (Fri, 18 Jan 2008) | 1 line
Added section on passing contextual information to logging and documentation for the LoggerAdapter class.
........
r60050 | vinay.sajip | 2008-01-18 16:55:57 +0100 (Fri, 18 Jan 2008) | 1 line
Added LoggerAdapter class, changed copyright dates, made check for extra parameter passed to logging methods explicitly against None rather than a truth value.
........
r60051 | georg.brandl | 2008-01-18 17:42:57 +0100 (Fri, 18 Jan 2008) | 2 lines
Note that genexps are function scopes too and therefore won't see class attributes.
........
r60052 | christian.heimes | 2008-01-18 19:24:07 +0100 (Fri, 18 Jan 2008) | 1 line
Added bytes and b'' as aliases for str and ''
........
Diffstat (limited to 'Doc/library')
-rw-r--r-- | Doc/library/logging.rst | 161 |
1 files changed, 134 insertions, 27 deletions
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index af8c867..258bb0a 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -1136,33 +1136,113 @@ 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 effectively unbounded. -There are a number of other ways you can pass contextual information to be -output along with logging event information. - -* Use an adapter class which has access to the contextual information and - which defines methods :meth:`debug`, :meth:`info` etc. with the same - signatures as used by :class:`Logger`. You instantiate the adapter with a - name, which will be used to create an underlying :class:`Logger` with that - name. In each adpater method, the passed-in message is modified to include - whatever contextual information you want. - -* Use something other than a string to pass the message. Although normally - the first argument to a logger method such as :meth:`debug`, :meth:`info` - etc. is usually a string, it can in fact be any object. This object is the - argument to a :func:`str()` call which is made, in - :meth:`LogRecord.getMessage`, to obtain the actual message string. You can - use this behavior to pass an instance which may be initialized with a - logging message, which redefines :meth:__str__ to return a modified version - of that message with the contextual information added. - -* Use a specialized :class:`Formatter` subclass to add additional information - to the formatted output. The subclass could, for instance, merge some thread - local contextual information (or contextual information obtained in some - other way) with the output generated by the base :class:`Formatter`. - -In each of these three approaches, thread locals can sometimes be a useful way -of passing contextual information without undue coupling between different -parts of your code. +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. +This class is designed to look like a :class:`Logger`, so that you can call +:meth:`debug`, :meth:`info`, :meth:`warning`, :meth:`error`, +:meth:`exception`, :meth:`critical` and :meth:`log`. These methods have the +same signatures as their counterparts in :class:`Logger`, so you can use the +two types of instances interchangeably. + +When you create an instance of :class:`LoggerAdapter`, you pass it a +:class:`Logger` instance and a dict-like object which contains your contextual +information. When you call one of the logging methods on an instance of +:class:`LoggerAdapter`, it delegates the call to the underlying instance of +:class:`Logger` passed to its constructor, and arranges to pass the contextual +information in the delegated call. Here's a snippet from the code of +:class:`LoggerAdapter`:: + + def debug(self, msg, *args, **kwargs): + """ + Delegate a debug call to the underlying logger, after adding + contextual information from this adapter instance. + """ + msg, kwargs = self.process(msg, kwargs) + self.logger.debug(msg, *args, **kwargs) + +The :meth:`process` method of :class:`LoggerAdapter` is where the contextual +information is added to the logging output. It's passed the message and +keyword arguments of the logging call, and it passes back (potentially) +modified versions of these to use in the call to the underlying logger. The +default implementation of this method leaves the message alone, but inserts +an "extra" key in the keyword argument whose value is the dict-like object +passed to the constructor. Of course, if you had passed an "extra" keyword +argument in the call to the adapter, it will be silently overwritten. + +The advantage of using "extra" is that the values in the dict-like object are +merged into the :class:`LogRecord` instance's __dict__, allowing you to use +customized strings with your :class:`Formatter` instances which know about +the keys of the dict-like object. If you need a different method, e.g. if you +want to prepend or append the contextual information to the message string, +you just need to subclass :class:`LoggerAdapter` and override :meth:`process` +to do what you need. Here's an example script which uses this class, which +also illustrates what dict-like behaviour is needed from an arbitrary +"dict-like" object for use in the constructor:: + +import logging + +class ConnInfo: + """ + An example class which shows how an arbitrary class can be used as + the 'extra' context information repository passed to a LoggerAdapter. + """ + + def __getitem__(self, name): + """ + To allow this instance to look like a dict. + """ + from random import choice + if name == "ip": + result = choice(["127.0.0.1", "192.168.0.1"]) + elif name == "user": + result = choice(["jim", "fred", "sheila"]) + else: + result = self.__dict__.get(name, "?") + return result + + def __iter__(self): + """ + To allow iteration over keys, which will be merged into + the LogRecord dict before formatting and output. + """ + keys = ["ip", "user"] + keys.extend(self.__dict__.keys()) + return keys.__iter__() + +if __name__ == "__main__": + from random import choice + 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.debug("A debug message") + a1.info("An info message with %s", "some parameters") + a2 = logging.LoggerAdapter(logging.getLogger("d.e.f"), ConnInfo()) + 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") + +When this script is run, the output should look something like this:: + +2008-01-18 14:49:54,023 a.b.c DEBUG IP: 123.231.231.123 User: sheila A debug message +2008-01-18 14:49:54,023 a.b.c INFO IP: 123.231.231.123 User: sheila An info message with some parameters +2008-01-18 14:49:54,023 d.e.f CRITICAL IP: 192.168.0.1 User: jim A message at CRITICAL level with 2 parameters +2008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: jim A message at INFO level with 2 parameters +2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters +2008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: fred A message at ERROR level with 2 parameters +2008-01-18 14:49:54,033 d.e.f ERROR IP: 127.0.0.1 User: sheila A message at ERROR level with 2 parameters +2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila A message at WARNING level with 2 parameters +2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: jim A message at WARNING level with 2 parameters +2008-01-18 14:49:54,033 d.e.f INFO IP: 192.168.0.1 User: fred A message at INFO level with 2 parameters +2008-01-18 14:49:54,033 d.e.f WARNING IP: 192.168.0.1 User: sheila 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 + +.. versionadded:: 2.6 + +The :class:`LoggerAdapter` class was not present in previous versions. + .. _network-logging: @@ -2047,6 +2127,33 @@ made, and any exception information to be logged. Returns the message for this :class:`LogRecord` instance after merging any user-supplied arguments with the message. +LoggerAdapter Objects +--------------------- + +.. versionadded:: 2.6 + +:class:`LoggerAdapter` instances are used to conveniently pass contextual +information into logging calls. For a usage example , see context-info_. + +.. class:: LoggerAdapter(logger, extra) + + Returns an instance of :class:`LoggerAdapter` initialized with an + underlying :class:`Logger` instance and a dict-like object. + +.. method:: LoggerAdapter.process(msg, kwargs) + + Modifies the message and/or keyword arguments passed to a logging call in + order to insert contextual information. This implementation takes the + object passed as *extra* to the constructor and adds it to *kwargs* using + key 'extra'. The return value is a (*msg*, *kwargs*) tuple which has the + (possibly modified) versions of the arguments passed in. + +In addition to the above, :class:`LoggerAdapter` supports all the logging +methods of :class:`Logger`, i.e. :meth:`debug`, :meth:`info`, :meth:`warning`, +:meth:`error`, :meth:`exception`, :meth:`critical` and :meth:`log`. These +methods have the same signatures as their counterparts in :class:`Logger`, so +you can use the two types of instances interchangeably. + Thread Safety ------------- |