8000 [3.11] gh-115233: Fix an example in the Logging Cookbook (GH-115325) … · python/cpython@21edde1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 21edde1

Browse files
[3.11] gh-115233: Fix an example in the Logging Cookbook (GH-115325) (GH-115355) (GH-115357)
Also add more tests for LoggerAdapter. Also support stacklevel in LoggerAdapter._log(). (cherry picked from commit 225856e) (cherry picked from commit 9182201) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent d732134 commit 21edde1

File tree

4 files changed

+106
-23
lines changed

4 files changed

+106
-23
lines changed

Doc/howto/logging-cookbook.rst

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,13 +1744,11 @@ to the above, as in the following example::
17441744
return self.fmt.format(*self.args)
17451745

17461746
class StyleAdapter(logging.LoggerAdapter):
1747-
def __init__(self, logger, extra=None):
1748-
super().__init__(logger, extra or {})
1749-
1750-
def log(self, level, msg, /, *args, **kwargs):
1747+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
17511748
if self.isEnabledFor(level):
17521749
msg, kwargs = self.process(msg, kwargs)
1753-
self.logger._log(level, Message(msg, args), (), **kwargs)
1750+
self.logger.log(level, Message(msg, args), **kwargs,
1751+
stacklevel=stacklevel+1)
17541752

17551753
logger = StyleAdapter(logging.getLogger(__name__))
17561754

@@ -1762,7 +1760,7 @@ to the above, as in the following example::
17621760
main()
17631761

17641762
The above script should log the message ``Hello, world!`` when run with
1765-
Python 3.2 or later.
1763+
Python 3.8 or later.
17661764

17671765

17681766
.. currentmodule:: logging

Lib/logging/__init__.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,18 +1910,11 @@ def hasHandlers(self):
19101910
"""
19111911
return self.logger.hasHandlers()
19121912

1913-
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
1913+
def _log(self, level, msg, args, **kwargs):
19141914
"""
19151915
Low-level log implementation, proxied to allow nested logger adapters.
19161916
"""
1917-
return self.logger._log(
1918-
level,
1919-
msg,
1920-
args,
1921-
exc_info=exc_info,
1922-
extra=extra,
1923-
stack_info=stack_info,
1924-
)
1917+
return self.logger._log(level, msg, args, **kwargs)
19251918

19261919
@property
19271920
def manager(self):

Lib/test/test_logging.py

E864
Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5075,6 +5075,7 @@ def test_critical(self):
50755075
self.assertEqual(record.levelno, logging.CRITICAL)
50765076
self.assertEqual(record.msg, msg)
50775077
self.assertEqual(record.args, (self.recording,))
5078+
self.assertEqual(record.funcName, 'test_critical')
50785079

50795080
def test_is_enabled_for(self):
50805081
old_disable = self.adapter.logger.manager.disable
@@ -5093,15 +5094,9 @@ def test_has_handlers(self):
50935094
self.assertFalse(self.adapter.hasHandlers())
50945095

50955096
def test_nested(self):
5096-
class Adapter(logging.LoggerAdapter):
5097-
prefix = 'Adapter'
5098-
5099-
def process(self, msg, kwargs):
5100-
return f"{self.prefix} {msg}", kwargs
5101-
51025097
msg = 'Adapters can be nested, yo.'
5103-
adapter = Adapter(logger=self.logger, extra=None)
5104-
adapter_adapter = Adapter(logger=adapter, extra=None)
5098+
adapter = PrefixAdapter(logger=self.logger, extra=None)
5099+
adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
51055100
adapter_adapter.prefix = 'AdapterAdapter'
51065101
self.assertEqual(repr(adapter), repr(adapter_adapter))
51075102
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
@@ -5110,6 +5105,7 @@ def process(self, msg, kwargs):
51105105
self.assertEqual(record.levelno, logging.CRITICAL)
51115106
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
51125107
self.assertEqual(record.args, (self.recording,))
5108+
self.assertEqual(record.funcName, 'test_nested')
51135109
orig_manager = adapter_adapter.manager
51145110
self.assertIs(adapter.manager, orig_manager)
51155111
self.assertIs(self.logger.manager, orig_manager)
@@ -5125,6 +5121,101 @@ def process(self, msg, kwargs):
51255121
self.assertIs(adapter.manager, orig_manager)
51265122
self.assertIs(self.logger.manager, orig_manager)
51275123

5124+
def test_styled_adapter(self):
5125+
# Test an example from the Cookbook.
5126+
records = self.recording.records
5127+
adapter = StyleAdapter(self.logger)
5128+
adapter.warning('Hello, {}!', 'world')
5129+
self.assertEqual(str(records[-1].msg), 'Hello, world!')
5130+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5131+
adapter.log(logging.WARNING, 'Goodbye {}.', 'world')
5132+
self.assertEqual(str(records[-1].msg), 'Goodbye world.')
5133+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5134+
5135+
def test_nested_styled_adapter(self):
5136+
records = self.recording.records
5137+
adapter = PrefixAdapter(self.logger)
5138+
adapter.prefix = '{}'
5139+
adapter2 = StyleAdapter(adapter)
5140+
adapter2.warning('Hello, {}!', 'world')
5141+
self.assertEqual(str(records[-1].msg), '{} Hello, world!')
5142+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5143+
adapter2.log(logging.WARNING, 'Goodbye {}.', 'world')
5144+
self.assertEqual(str(records[-1].msg), '{} Goodbye world.')
5145+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5146+
5147+
def test_find_caller_with_stacklevel(self):
5148+
the_level = 1
5149+
trigger = self.adapter.warning
5150+
5151+
def innermost():
5152+
trigger('test', stacklevel=the_level)
5153+
5154+
def inner():
5155+
innermost()
5156+
5157+
def outer():
5158+
inner()
5159+
5160+
records = self.recording.records
5161+
outer()
5162+
self.assertEqual(records[-1].funcName, 'innermost')
5163+
lineno = records[-1].lineno
5164+
the_level += 1
5165+
outer()
5166+
self.assertEqual(records[-1].funcName, 'inner')
5167+
self.assertGreater(records[-1].lineno, lineno)
5168+
lineno = records[-1].lineno
5169+
the_level += 1
5170+
outer()
5171+
self.assertEqual(records[-1].funcName, 'outer')
5172+
self.assertGreater(records[-1].lineno, lineno)
5173+
lineno = records[-1].lineno
5174+
the_level += 1
5175+
outer()
5176+
self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5177+
self.assertGreater(records[-1].lineno, lineno)
5178+
5179+
def test_extra_in_records(self):
5180+
self.adapter = logging.LoggerAdapter(logger=self.logger,
5181+
extra={'foo': '1'})
5182+
5183+
self.adapter.critical('foo should be here')
5184+
self.assertEqual(len(self.recording.records), 1)
5185+
record = self.recording.records[0]
5186+
self.assertTrue(hasattr(record, 'foo'))
5187+
self.assertEqual(record.foo, '1')
5188+
5189+
def test_extra_not_merged_by_default(self):
5190+
self.adapter.critical('foo should NOT be here', extra={'foo': 'nope'})
5191+
self.assertEqual(len(self.recording.records), 1)
5192+
record = self.recording.records[0]
5193+
self.assertFalse(hasattr(record, 'foo'))
5194+
5195+
5196+
class PrefixAdapter(logging.LoggerAdapter):
5197+
prefix = 'Adapter'
5198+
5199+
def process(self, msg, kwargs):
5200+
return f"{self.prefix} {msg}", kwargs
5201+
5202+
5203+
class Message:
5204+
def __init__(self, fmt, args):
5205+
self.fmt = fmt
5206+
self.args = args
5207+
5208+
def __str__(self):
5209+
return self.fmt.format(*self.args)
5210+
5211+
5212+
class StyleAdapter(logging.LoggerAdapter):
5213+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
5214+
if self.isEnabledFor(level):
5215+
msg, kwargs = self.process(msg, kwargs)
5216+
self.logger.log(level, Message(msg, args), **kwargs,
5217+
stacklevel=stacklevel+1)
5218+
51285219

51295220
class LoggerTest(BaseTest, AssertErrorMessage):
51305221

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix an example for :class:`~logging.LoggerAdapter` in the Logging Cookbook.

0 commit comments

Comments
 (0)
0