summaryrefslogtreecommitdiffstats
path: root/Doc/howto/logging-cookbook.rst
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2012-02-27 11:57:25 (GMT)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2012-02-27 11:57:25 (GMT)
commitbd2c199b2b3b0a60a1feb9e16063ca3d83322137 (patch)
tree22c071748ccac6ec8e49d1f6c4dddeb3c5a07b2f /Doc/howto/logging-cookbook.rst
parent01121f2d5ab06ae5d4771df7ce7b75e520f4a76c (diff)
parent982f534fabcc0e94590080d9283703175ddc91ee (diff)
downloadcpython-bd2c199b2b3b0a60a1feb9e16063ca3d83322137.zip
cpython-bd2c199b2b3b0a60a1feb9e16063ca3d83322137.tar.gz
cpython-bd2c199b2b3b0a60a1feb9e16063ca3d83322137.tar.bz2
Merged cookbook update for LogRecord customisation.
Diffstat (limited to 'Doc/howto/logging-cookbook.rst')
-rw-r--r--Doc/howto/logging-cookbook.rst82
1 files changed, 82 insertions, 0 deletions
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index dee29e2..536135b 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -1092,6 +1092,88 @@ string. That's because the __ notation is just syntax sugar for a constructor
call to one of the XXXMessage classes.
+.. currentmodule:: logging
+
+.. custom-logrecord:
+
+Customising ``LogRecord``
+-------------------------
+
+Every logging event is represented by a :class:`LogRecord` instance.
+When an event is logged and not filtered out by a logger's level, a
+:class:`LogRecord` is created, populated with information about the event and
+then passed to the handlers for that logger (and its ancestors, up to and
+including the logger where further propagation up the hierarchy is disabled).
+Before Python 3.2, there were only two places where this creation was done:
+
+* :meth:`Logger.makeRecord`, which is called in the normal process of
+ logging an event. This invoked :class:`LogRecord` directly to create an
+ instance.
+* :func:`makeLogRecord`, which is called with a dictionary containing
+ attributes to be added to the LogRecord. This is typically invoked when a
+ suitable dictionary has been received over the network (e.g. in pickle form
+ via a :class:`~handlers.SocketHandler`, or in JSON form via an
+ :class:`~handlers.HTTPHandler`).
+
+This has usually meant that if you need to do anything special with a
+:class:`LogRecord`, you've had to do one of the following.
+
+* Create your own :class:`Logger` subclass, which overrides
+ :meth:`Logger.makeRecord`, and set it using :func:`~logging.setLoggerClass`
+ before any loggers that you care about are instantiated.
+* Add a :class:`Filter` to a logger or handler, which does the
+ necessary special manipulation you need when its
+ :meth:`~Filter.filter` method is called.
+
+The first approach would be a little unwieldy in the scenario where (say)
+several different libraries wanted to do different things. Each would attempt
+to set its own :class:`Logger` subclass, and the one which did this last would
+win.
+
+The second approach works reasonably well for many cases, but does not allow
+you to e.g. use a specialized subclass of :class:`LogRecord`. Library
+developers can set a suitable filter on their loggers, but they would have to
+remember to do this every time they introduced a new logger (which they would
+do simply by adding new packages or modules and doing
+
+.. code-block:: python
+
+ logger = logging.getLogger(__name__)
+
+at module level). It's probably one too many things to think about. Developers
+could also add the filter to a :class:`~logging.NullHandler` attached to their
+top-level logger, but this would not be invoked if an application developer
+attached a handler to a lower-level library logger – so output from that
+handler would not reflect the intentions of the library developer.
+
+In Python 3.2 and later, :class:`~logging.LogRecord` creation is done through a
+factory, which you can specify. The factory is just a callable you can set with
+:func:`~logging.setLogRecordFactory`, and interrogate with
+:func:`~logging.getLogRecordFactory`. The factory is invoked with the same
+signature as the :class:`~logging.LogRecord` constructor, as :class:`LogRecord`
+is the default setting for the factory.
+
+This approach allows a custom factory to control all aspects of LogRecord
+creation. For example, you could return a subclass, or just add some additional
+attributes to the record once created, using a pattern similar to this::
+
+ old_factory = logging.getLogRecordFactory()
+
+ def record_factory(*args, **kwargs):
+ record = old_factory(*args, **kwargs)
+ record.custom_attribute = 0xdecafbad
+ return record
+
+ logging.setLogRecordFactory(record_factory)
+
+This pattern allows different libraries to chain factories together, and as
+long as they don't overwrite each other's attributes or unintentionally
+overwrite the attributes provided as standard, there should be no surprises.
+However, it should be borne in mind that each link in the chain adds run-time
+overhead to all logging operations, and the technique should only be used when
+the use of a :class:`Filter` does not provide the desired result.
+
+
.. _zeromq-handlers:
Subclassing QueueHandler - a ZeroMQ example