Skip to content

Commit 327dbbe

Browse files
gh-138162: Fix logging.LoggerAdapter with merge_extra=True and without the extra argument (GH-140511)
1 parent 622d97b commit 327dbbe

File tree

4 files changed

+50
-11
lines changed

4 files changed

+50
-11
lines changed

Doc/library/logging.rst

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,12 +1082,13 @@ LoggerAdapter Objects
10821082
information into logging calls. For a usage example, see the section on
10831083
:ref:`adding contextual information to your logging output <context-info>`.
10841084

1085-
.. class:: LoggerAdapter(logger, extra, merge_extra=False)
1085+
.. class:: LoggerAdapter(logger, extra=None, merge_extra=False)
10861086

10871087
Returns an instance of :class:`LoggerAdapter` initialized with an
1088-
underlying :class:`Logger` instance, a dict-like object (*extra*), and a
1089-
boolean (*merge_extra*) indicating whether or not the *extra* argument of
1090-
individual log calls should be merged with the :class:`LoggerAdapter` extra.
1088+
underlying :class:`Logger` instance, an optional dict-like object (*extra*),
1089+
and an optional boolean (*merge_extra*) indicating whether or not
1090+
the *extra* argument of individual log calls should be merged with
1091+
the :class:`LoggerAdapter` extra.
10911092
The default behavior is to ignore the *extra* argument of individual log
10921093
calls and only use the one of the :class:`LoggerAdapter` instance
10931094

@@ -1127,9 +1128,13 @@ information into logging calls. For a usage example, see the section on
11271128
Attribute :attr:`!manager` and method :meth:`!_log` were added, which
11281129
delegate to the underlying logger and allow adapters to be nested.
11291130

1131+
.. versionchanged:: 3.10
1132+
1133+
The *extra* argument is now optional.
1134+
11301135
.. versionchanged:: 3.13
11311136

1132-
The *merge_extra* argument was added.
1137+
The *merge_extra* parameter was added.
11331138

11341139

11351140
Thread Safety

Lib/logging/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,9 +1849,9 @@ class LoggerAdapter(object):
18491849

18501850
def __init__(self, logger, extra=None, merge_extra=False):
18511851
"""
1852-
Initialize the adapter with a logger and a dict-like object which
1853-
provides contextual information. This constructor signature allows
1854-
easy stacking of LoggerAdapters, if so desired.
1852+
Initialize the adapter with a logger and an optional dict-like object
1853+
which provides contextual information. This constructor signature
1854+
allows easy stacking of LoggerAdapters, if so desired.
18551855
18561856
You can effectively pass keyword arguments as shown in the
18571857
following example:
@@ -1882,8 +1882,9 @@ def process(self, msg, kwargs):
18821882
Normally, you'll only need to override this one method in a
18831883
LoggerAdapter subclass for your specific needs.
18841884
"""
1885-
if self.merge_extra and "extra" in kwargs:
1886-
kwargs["extra"] = {**self.extra, **kwargs["extra"]}
1885+
if self.merge_extra and kwargs.get("extra") is not None:
1886+
if self.extra is not None:
1887+
kwargs["extra"] = {**self.extra, **kwargs["extra"]}
18871888
else:
18881889
kwargs["extra"] = self.extra
18891890
return msg, kwargs

Lib/test/test_logging.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5826,7 +5826,7 @@ def cleanup():
58265826

58275827
self.addCleanup(cleanup)
58285828
self.addCleanup(logging.shutdown)
5829-
self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
5829+
self.adapter = logging.LoggerAdapter(logger=self.logger)
58305830

58315831
def test_exception(self):
58325832
msg = 'testing exception: %r'
@@ -5997,6 +5997,18 @@ def test_extra_merged(self):
59975997
self.assertEqual(record.foo, '1')
59985998
self.assertEqual(record.bar, '2')
59995999

6000+
self.adapter.critical('no extra') # should not fail
6001+
self.assertEqual(len(self.recording.records), 2)
6002+
record = self.recording.records[-1]
6003+
self.assertEqual(record.foo, '1')
6004+
self.assertNotHasAttr(record, 'bar')
6005+
6006+
self.adapter.critical('none extra', extra=None) # should not fail
6007+
self.assertEqual(len(self.recording.records), 3)
6008+
record = self.recording.records[-1]
6009+
self.assertEqual(record.foo, '1')
6010+
self.assertNotHasAttr(record, 'bar')
6011+
60006012
def test_extra_merged_log_call_has_precedence(self):
60016013
self.adapter = logging.LoggerAdapter(logger=self.logger,
60026014
extra={'foo': '1'},
@@ -6008,6 +6020,25 @@ def test_extra_merged_log_call_has_precedence(self):
60086020
self.assertHasAttr(record, 'foo')
60096021
self.assertEqual(record.foo, '2')
60106022

6023+
def test_extra_merged_without_extra(self):
6024+
self.adapter = logging.LoggerAdapter(logger=self.logger,
6025+
merge_extra=True)
6026+
6027+
self.adapter.critical('foo should be here', extra={'foo': '1'})
6028+
self.assertEqual(len(self.recording.records), 1)
6029+
record = self.recording.records[-1]
6030+
self.assertEqual(record.foo, '1')
6031+
6032+
self.adapter.critical('no extra') # should not fail
6033+
self.assertEqual(len(self.recording.records), 2)
6034+
record = self.recording.records[-1]
6035+
self.assertNotHasAttr(record, 'foo')
6036+
6037+
self.adapter.critical('none extra', extra=None) # should not fail
6038+
self.assertEqual(len(self.recording.records), 3)
6039+
record = self.recording.records[-1]
6040+
self.assertNotHasAttr(record, 'foo')
6041+
60116042

60126043
class PrefixAdapter(logging.LoggerAdapter):
60136044
prefix = 'Adapter'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :class:`logging.LoggerAdapter` with ``merge_extra=True`` and without the
2+
*extra* argument.

0 commit comments

Comments
 (0)