Skip to content

Commit 9182201

Browse files
pythongh-115233: Fix an example in the Logging Cookbook (pythonGH-115325)
Also add more tests for LoggerAdapter. Also support stacklevel in LoggerAdapter._log().
1 parent 8144661 commit 9182201

File tree

4 files changed

+90
-23
lines changed

4 files changed

+90
-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
@@ -1949,18 +1949,11 @@ def hasHandlers(self):
19491949
"""
19501950
return self.logger.hasHandlers()
19511951

1952-
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
1952+
def _log(self, level, msg, args, **kwargs):
19531953
"""
19541954
Low-level log implementation, proxied to allow nested logger adapters.
19551955
"""
1956-
return self.logger._log(
1957-
level,
1958-
msg,
1959-
args,
1960-
exc_info=exc_info,
1961-
extra=extra,
1962-
stack_info=stack_info,
1963-
)
1956+
return self.logger._log(level, msg, args, **kwargs)
19641957

19651958
@property
19661959
def manager(self):

Lib/test/test_logging.py

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5478,6 +5478,7 @@ def test_critical(self):
54785478
self.assertEqual(record.levelno, logging.CRITICAL)
54795479
self.assertEqual(record.msg, msg)
54805480
self.assertEqual(record.args, (self.recording,))
5481+
self.assertEqual(record.funcName, 'test_critical')
54815482

54825483
def test_is_enabled_for(self):
54835484
old_disable = self.adapter.logger.manager.disable
@@ -5496,15 +5497,9 @@ def test_has_handlers(self):
54965497
self.assertFalse(self.adapter.hasHandlers())
54975498

54985499
def test_nested(self):
5499-
class Adapter(logging.LoggerAdapter):
5500-
prefix = 'Adapter'
5501-
5502-
def process(self, msg, kwargs):
5503-
return f"{self.prefix} {msg}", kwargs
5504-
55055500
msg = 'Adapters can be nested, yo.'
5506-
adapter = Adapter(logger=self.logger, extra=None)
5507-
adapter_adapter = Adapter(logger=adapter, extra=None)
5501+
adapter = PrefixAdapter(logger=self.logger, extra=None)
5502+
adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
55085503
adapter_adapter.prefix = 'AdapterAdapter'
55095504
self.assertEqual(repr(adapter), repr(adapter_adapter))
55105505
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
@@ -5513,6 +5508,7 @@ def process(self, msg, kwargs):
55135508
self.assertEqual(record.levelno, logging.CRITICAL)
55145509
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
55155510
self.assertEqual(record.args, (self.recording,))
5511+
self.assertEqual(record.funcName, 'test_nested')
55165512
orig_manager = adapter_adapter.manager
55175513
self.assertIs(adapter.manager, orig_manager)
55185514
self.assertIs(self.logger.manager, orig_manager)
@@ -5528,6 +5524,61 @@ def process(self, msg, kwargs):
55285524
self.assertIs(adapter.manager, orig_manager)
55295525
self.assertIs(self.logger.manager, orig_manager)
55305526

5527+
def test_styled_adapter(self):
5528+
# Test an example from the Cookbook.
5529+
records = self.recording.records
5530+
adapter = StyleAdapter(self.logger)
5531+
adapter.warning('Hello, {}!', 'world')
5532+
self.assertEqual(str(records[-1].msg), 'Hello, world!')
5533+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5534+
adapter.log(logging.WARNING, 'Goodbye {}.', 'world')
5535+
self.assertEqual(str(records[-1].msg), 'Goodbye world.')
5536+
self.assertEqual(records[-1].funcName, 'test_styled_adapter')
5537+
5538+
def test_nested_styled_adapter(self):
5539+
records = self.recording.records
5540+
adapter = PrefixAdapter(self.logger)
5541+
adapter.prefix = '{}'
5542+
adapter2 = StyleAdapter(adapter)
5543+
adapter2.warning('Hello, {}!', 'world')
5544+
self.assertEqual(str(records[-1].msg), '{} Hello, world!')
5545+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5546+
adapter2.log(logging.WARNING, 'Goodbye {}.', 'world')
5547+
self.assertEqual(str(records[-1].msg), '{} Goodbye world.')
5548+
self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
5549+
5550+
def test_find_caller_with_stacklevel(self):
5551+
the_level = 1
5552+
trigger = self.adapter.warning
5553+
5554+
def innermost():
5555+
trigger('test', stacklevel=the_level)
5556+
5557+
def inner():
5558+
innermost()
5559+
5560+
def outer():
5561+
inner()
5562+
5563+
records = self.recording.records
5564+
outer()
5565+
self.assertEqual(records[-1].funcName, 'innermost')
5566+
lineno = records[-1].lineno
5567+
the_level += 1
5568+
outer()
5569+
self.assertEqual(records[-1].funcName, 'inner')
5570+
self.assertGreater(records[-1].lineno, lineno)
5571+
lineno = records[-1].lineno
5572+
the_level += 1
5573+
outer()
5574+
self.assertEqual(records[-1].funcName, 'outer')
5575+
self.assertGreater(records[-1].lineno, lineno)
5576+
lineno = records[-1].lineno
5577+
the_level += 1
5578+
outer()
5579+
self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5580+
self.assertGreater(records[-1].lineno, lineno)
5581+
55315582
def test_extra_in_records(self):
55325583
self.adapter = logging.LoggerAdapter(logger=self.logger,
55335584
extra={'foo': '1'})
@@ -5569,6 +5620,30 @@ def test_extra_merged_log_call_has_precedence(self):
55695620
self.assertEqual(record.foo, '2')
55705621

55715622

5623+
class PrefixAdapter(logging.LoggerAdapter):
5624+
prefix = 'Adapter'
5625+
5626+
def process(self, msg, kwargs):
5627+
return f"{self.prefix} {msg}", kwargs
5628+
5629+
5630+
class Message:
5631+
def __init__(self, fmt, args):
5632+
self.fmt = fmt
5633+
self.args = args
5634+
5635+
def __str__(self):
5636+
return self.fmt.format(*self.args)
5637+
5638+
5639+
class StyleAdapter(logging.LoggerAdapter):
5640+
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
5641+
if self.isEnabledFor(level):
5642+
msg, kwargs = self.process(msg, kwargs)
5643+
self.logger.log(level, Message(msg, args), **kwargs,
5644+
stacklevel=stacklevel+1)
5645+
5646+
55725647
class LoggerTest(BaseTest, AssertErrorMessage):
55735648

55745649
def setUp(self):
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)