information into logging calls. For a usage example, see the section on
:ref:`adding contextual information to your logging output <context-info>`.
-.. class:: LoggerAdapter(logger, extra, merge_extra=False)
+.. class:: LoggerAdapter(logger, extra=None, merge_extra=False)
Returns an instance of :class:`LoggerAdapter` initialized with an
- underlying :class:`Logger` instance, a dict-like object (*extra*), and a
- boolean (*merge_extra*) indicating whether or not the *extra* argument of
- individual log calls should be merged with the :class:`LoggerAdapter` extra.
+ underlying :class:`Logger` instance, an optional dict-like object (*extra*),
+ and an optional boolean (*merge_extra*) indicating whether or not
+ the *extra* argument of individual log calls should be merged with
+ the :class:`LoggerAdapter` extra.
The default behavior is to ignore the *extra* argument of individual log
calls and only use the one of the :class:`LoggerAdapter` instance
Attribute :attr:`!manager` and method :meth:`!_log` were added, which
delegate to the underlying logger and allow adapters to be nested.
+ .. versionchanged:: 3.10
+
+ The *extra* argument is now optional.
+
.. versionchanged:: 3.13
- The *merge_extra* argument was added.
+ The *merge_extra* parameter was added.
Thread Safety
def __init__(self, logger, extra=None, merge_extra=False):
"""
- Initialize the adapter with a logger and a dict-like object which
- provides contextual information. This constructor signature allows
- easy stacking of LoggerAdapters, if so desired.
+ Initialize the adapter with a logger and an optional dict-like object
+ which provides contextual information. This constructor signature
+ allows easy stacking of LoggerAdapters, if so desired.
You can effectively pass keyword arguments as shown in the
following example:
Normally, you'll only need to override this one method in a
LoggerAdapter subclass for your specific needs.
"""
- if self.merge_extra and "extra" in kwargs:
- kwargs["extra"] = {**self.extra, **kwargs["extra"]}
+ if self.merge_extra and kwargs.get("extra") is not None:
+ if self.extra is not None:
+ kwargs["extra"] = {**self.extra, **kwargs["extra"]}
else:
kwargs["extra"] = self.extra
return msg, kwargs
from test.support import asyncore
from test.support import smtpd
from test.support.logging_helper import TestHandler
+from test.support.testcase import ExtraAssertions
import textwrap
import threading
import asyncio
self._test_log('critical')
-class LoggerAdapterTest(unittest.TestCase):
+class LoggerAdapterTest(unittest.TestCase, ExtraAssertions):
def setUp(self):
super(LoggerAdapterTest, self).setUp()
old_handler_list = logging._handlerList[:]
self.addCleanup(cleanup)
self.addCleanup(logging.shutdown)
- self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
+ self.adapter = logging.LoggerAdapter(logger=self.logger)
def test_exception(self):
msg = 'testing exception: %r'
self.assertEqual(record.foo, '1')
self.assertEqual(record.bar, '2')
+ self.adapter.critical('no extra') # should not fail
+ self.assertEqual(len(self.recording.records), 2)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+ self.assertNotHasAttr(record, 'bar')
+
+ self.adapter.critical('none extra', extra=None) # should not fail
+ self.assertEqual(len(self.recording.records), 3)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+ self.assertNotHasAttr(record, 'bar')
+
def test_extra_merged_log_call_has_precedence(self):
self.adapter = logging.LoggerAdapter(logger=self.logger,
extra={'foo': '1'},
self.assertTrue(hasattr(record, 'foo'))
self.assertEqual(record.foo, '2')
+ def test_extra_merged_without_extra(self):
+ self.adapter = logging.LoggerAdapter(logger=self.logger,
+ merge_extra=True)
+
+ self.adapter.critical('foo should be here', extra={'foo': '1'})
+ self.assertEqual(len(self.recording.records), 1)
+ record = self.recording.records[-1]
+ self.assertEqual(record.foo, '1')
+
+ self.adapter.critical('no extra') # should not fail
+ self.assertEqual(len(self.recording.records), 2)
+ record = self.recording.records[-1]
+ self.assertNotHasAttr(record, 'foo')
+
+ self.adapter.critical('none extra', extra=None) # should not fail
+ self.assertEqual(len(self.recording.records), 3)
+ record = self.recording.records[-1]
+ self.assertNotHasAttr(record, 'foo')
+
class PrefixAdapter(logging.LoggerAdapter):
prefix = 'Adapter'