Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Doc/library/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1082,12 +1082,13 @@ LoggerAdapter Objects
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

Expand Down Expand Up @@ -1127,9 +1128,13 @@ information into logging calls. For a usage example, see the section on
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
Expand Down
11 changes: 6 additions & 5 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1852,9 +1852,9 @@ class LoggerAdapter(object):

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:
Expand Down Expand Up @@ -1885,8 +1885,9 @@ def process(self, msg, kwargs):
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
Expand Down
33 changes: 32 additions & 1 deletion Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5800,7 +5800,7 @@ def cleanup():

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'
Expand Down Expand Up @@ -5971,6 +5971,18 @@ def test_extra_merged(self):
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'},
Expand All @@ -5982,6 +5994,25 @@ def test_extra_merged_log_call_has_precedence(self):
self.assertHasAttr(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'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :class:`logging.LoggerAdapter` with ``merge_extra=True`` and without the
*extra* argument.
Loading