Skip to content

Commit 60eb51c

Browse files
serhiy-storchakamiss-islington
authored andcommitted
pythongh-138162: Fix logging.LoggerAdapter with merge_extra=True and without the extra argument (pythonGH-140511)
(cherry picked from commit 327dbbe) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent 08cacb2 commit 60eb51c

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
@@ -5739,7 +5739,7 @@ def cleanup():
57395739

57405740
self.addCleanup(cleanup)
57415741
self.addCleanup(logging.shutdown)
5742-
self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
5742+
self.adapter = logging.LoggerAdapter(logger=self.logger)
57435743

57445744
def test_exception(self):
57455745
msg = 'testing exception: %r'
@@ -5910,6 +5910,18 @@ def test_extra_merged(self):
59105910
self.assertEqual(record.foo, '1')
59115911
self.assertEqual(record.bar, '2')
59125912

5913+
self.adapter.critical('no extra') # should not fail
5914+
self.assertEqual(len(self.recording.records), 2)
5915+
record = self.recording.records[-1]
5916+
self.assertEqual(record.foo, '1')
5917+
self.assertNotHasAttr(record, 'bar')
5918+
5919+
self.adapter.critical('none extra', extra=None) # should not fail
5920+
self.assertEqual(len(self.recording.records), 3)
5921+
record = self.recording.records[-1]
5922+
self.assertEqual(record.foo, '1')
5923+
self.assertNotHasAttr(record, 'bar')
5924+
59135925
def test_extra_merged_log_call_has_precedence(self):
59145926
self.adapter = logging.LoggerAdapter(logger=self.logger,
59155927
extra={'foo': '1'},
@@ -5921,6 +5933,25 @@ def test_extra_merged_log_call_has_precedence(self):
59215933
self.assertTrue(hasattr(record, 'foo'))
59225934
self.assertEqual(record.foo, '2')
59235935

5936+
def test_extra_merged_without_extra(self):
5937+
self.adapter = logging.LoggerAdapter(logger=self.logger,
5938+
merge_extra=True)
5939+
5940+
self.adapter.critical('foo should be here', extra={'foo': '1'})
5941+
self.assertEqual(len(self.recording.records), 1)
5942+
record = self.recording.records[-1]
5943+
self.assertEqual(record.foo, '1')
5944+
5945+
self.adapter.critical('no extra') # should not fail
5946+
self.assertEqual(len(self.recording.records), 2)
5947+
record = self.recording.records[-1]
5948+
self.assertNotHasAttr(record, 'foo')
5949+
5950+
self.adapter.critical('none extra', extra=None) # should not fail
5951+
self.assertEqual(len(self.recording.records), 3)
5952+
record = self.recording.records[-1]
5953+
self.assertNotHasAttr(record, 'foo')
5954+
59245955

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