Skip to content

Commit 6bb49ae

Browse files
[3.14] gh-138162: Fix logging.LoggerAdapter with merge_extra=True and without the extra argument (GH-140511) (GH-140784)
(cherry picked from commit 327dbbe) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 6e70c75 commit 6bb49ae

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
@@ -1852,9 +1852,9 @@ class LoggerAdapter(object):
18521852

18531853
def __init__(self, logger, extra=None, merge_extra=False):
18541854
"""
1855-
Initialize the adapter with a logger and a dict-like object which
1856-
provides contextual information. This constructor signature allows
1857-
easy stacking of LoggerAdapters, if so desired.
1855+
Initialize the adapter with a logger and an optional dict-like object
1856+
which provides contextual information. This constructor signature
1857+
allows easy stacking of LoggerAdapters, if so desired.
18581858
18591859
You can effectively pass keyword arguments as shown in the
18601860
following example:
@@ -1885,8 +1885,9 @@ def process(self, msg, kwargs):
18851885
Normally, you'll only need to override this one method in a
18861886
LoggerAdapter subclass for your specific needs.
18871887
"""
1888-
if self.merge_extra and "extra" in kwargs:
1889-
kwargs["extra"] = {**self.extra, **kwargs["extra"]}
1888+
if self.merge_extra and kwargs.get("extra") is not None:
1889+
if self.extra is not None:
1890+
kwargs["extra"] = {**self.extra, **kwargs["extra"]}
18901891
else:
18911892
kwargs["extra"] = self.extra
18921893
return msg, kwargs

Lib/test/test_logging.py

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

58015801
self.addCleanup(cleanup)
58025802
self.addCleanup(logging.shutdown)
5803-
self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
5803+
self.adapter = logging.LoggerAdapter(logger=self.logger)
58045804

58055805
def test_exception(self):
58065806
msg = 'testing exception: %r'
@@ -5971,6 +5971,18 @@ def test_extra_merged(self):
59715971
self.assertEqual(record.foo, '1')
59725972
self.assertEqual(record.bar, '2')
59735973

5974+
self.adapter.critical('no extra') # should not fail
5975+
self.assertEqual(len(self.recording.records), 2)
5976+
record = self.recording.records[-1]
5977+
self.assertEqual(record.foo, '1')
5978+
self.assertNotHasAttr(record, 'bar')
5979+
5980+
self.adapter.critical('none extra', extra=None) # should not fail
5981+
self.assertEqual(len(self.recording.records), 3)
5982+
record = self.recording.records[-1]
5983+
self.assertEqual(record.foo, '1')
5984+
self.assertNotHasAttr(record, 'bar')
5985+
59745986
def test_extra_merged_log_call_has_precedence(self):
59755987
self.adapter = logging.LoggerAdapter(logger=self.logger,
59765988
extra={'foo': '1'},
@@ -5982,6 +5994,25 @@ def test_extra_merged_log_call_has_precedence(self):
59825994
self.assertHasAttr(record, 'foo')
59835995
self.assertEqual(record.foo, '2')
59845996

5997+
def test_extra_merged_without_extra(self):
5998+
self.adapter = logging.LoggerAdapter(logger=self.logger,
5999+
merge_extra=True)
6000+
6001+
self.adapter.critical('foo should be here', extra={'foo': '1'})
6002+
self.assertEqual(len(self.recording.records), 1)
6003+
record = self.recording.records[-1]
6004+
self.assertEqual(record.foo, '1')
6005+
6006+
self.adapter.critical('no extra') # should not fail
6007+
self.assertEqual(len(self.recording.records), 2)
6008+
record = self.recording.records[-1]
6009+
self.assertNotHasAttr(record, 'foo')
6010+
6011+
self.adapter.critical('none extra', extra=None) # should not fail
6012+
self.assertEqual(len(self.recording.records), 3)
6013+
record = self.recording.records[-1]
6014+
self.assertNotHasAttr(record, 'foo')
6015+
59856016

59866017
class PrefixAdapter(logging.LoggerAdapter):
59876018
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)