summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2024-02-12 16:24:45 (GMT)
committerGitHub <noreply@github.com>2024-02-12 16:24:45 (GMT)
commit91822018eeba12a6c9eabbc748363b2fd4291b30 (patch)
tree048ca3476c3f2bccf0ce450c2d23e3b14bcffe7a
parent814466101790d4381ca4800c3d3b0cc0aad50c62 (diff)
downloadcpython-91822018eeba12a6c9eabbc748363b2fd4291b30.zip
cpython-91822018eeba12a6c9eabbc748363b2fd4291b30.tar.gz
cpython-91822018eeba12a6c9eabbc748363b2fd4291b30.tar.bz2
gh-115233: Fix an example in the Logging Cookbook (GH-115325)
Also add more tests for LoggerAdapter. Also support stacklevel in LoggerAdapter._log().
-rw-r--r--Doc/howto/logging-cookbook.rst10
-rw-r--r--Lib/logging/__init__.py11
-rw-r--r--Lib/test/test_logging.py91
-rw-r--r--Misc/NEWS.d/next/Documentation/2024-02-12-12-26-17.gh-issue-115233.aug6r9.rst1
4 files changed, 90 insertions, 23 deletions
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index 80147e3..f7d885e 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -1744,13 +1744,11 @@ to the above, as in the following example::
return self.fmt.format(*self.args)
class StyleAdapter(logging.LoggerAdapter):
- def __init__(self, logger, extra=None):
- super().__init__(logger, extra or {})
-
- def log(self, level, msg, /, *args, **kwargs):
+ def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
if self.isEnabledFor(level):
msg, kwargs = self.process(msg, kwargs)
- self.logger._log(level, Message(msg, args), (), **kwargs)
+ self.logger.log(level, Message(msg, args), **kwargs,
+ stacklevel=stacklevel+1)
logger = StyleAdapter(logging.getLogger(__name__))
@@ -1762,7 +1760,7 @@ to the above, as in the following example::
main()
The above script should log the message ``Hello, world!`` when run with
-Python 3.2 or later.
+Python 3.8 or later.
.. currentmodule:: logging
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 684b58d..fcec9e7 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1949,18 +1949,11 @@ class LoggerAdapter(object):
"""
return self.logger.hasHandlers()
- def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
+ def _log(self, level, msg, args, **kwargs):
"""
Low-level log implementation, proxied to allow nested logger adapters.
"""
- return self.logger._log(
- level,
- msg,
- args,
- exc_info=exc_info,
- extra=extra,
- stack_info=stack_info,
- )
+ return self.logger._log(level, msg, args, **kwargs)
@property
def manager(self):
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 8885232..cf09bad 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -5478,6 +5478,7 @@ class LoggerAdapterTest(unittest.TestCase):
self.assertEqual(record.levelno, logging.CRITICAL)
self.assertEqual(record.msg, msg)
self.assertEqual(record.args, (self.recording,))
+ self.assertEqual(record.funcName, 'test_critical')
def test_is_enabled_for(self):
old_disable = self.adapter.logger.manager.disable
@@ -5496,15 +5497,9 @@ class LoggerAdapterTest(unittest.TestCase):
self.assertFalse(self.adapter.hasHandlers())
def test_nested(self):
- class Adapter(logging.LoggerAdapter):
- prefix = 'Adapter'
-
- def process(self, msg, kwargs):
- return f"{self.prefix} {msg}", kwargs
-
msg = 'Adapters can be nested, yo.'
- adapter = Adapter(logger=self.logger, extra=None)
- adapter_adapter = Adapter(logger=adapter, extra=None)
+ adapter = PrefixAdapter(logger=self.logger, extra=None)
+ adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
adapter_adapter.prefix = 'AdapterAdapter'
self.assertEqual(repr(adapter), repr(adapter_adapter))
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
@@ -5513,6 +5508,7 @@ class LoggerAdapterTest(unittest.TestCase):
self.assertEqual(record.levelno, logging.CRITICAL)
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
self.assertEqual(record.args, (self.recording,))
+ self.assertEqual(record.funcName, 'test_nested')
orig_manager = adapter_adapter.manager
self.assertIs(adapter.manager, orig_manager)
self.assertIs(self.logger.manager, orig_manager)
@@ -5528,6 +5524,61 @@ class LoggerAdapterTest(unittest.TestCase):
self.assertIs(adapter.manager, orig_manager)
self.assertIs(self.logger.manager, orig_manager)
+ def test_styled_adapter(self):
+ # Test an example from the Cookbook.
+ records = self.recording.records
+ adapter = StyleAdapter(self.logger)
+ adapter.warning('Hello, {}!', 'world')
+ self.assertEqual(str(records[-1].msg), 'Hello, world!')
+ self.assertEqual(records[-1].funcName, 'test_styled_adapter')
+ adapter.log(logging.WARNING, 'Goodbye {}.', 'world')
+ self.assertEqual(str(records[-1].msg), 'Goodbye world.')
+ self.assertEqual(records[-1].funcName, 'test_styled_adapter')
+
+ def test_nested_styled_adapter(self):
+ records = self.recording.records
+ adapter = PrefixAdapter(self.logger)
+ adapter.prefix = '{}'
+ adapter2 = StyleAdapter(adapter)
+ adapter2.warning('Hello, {}!', 'world')
+ self.assertEqual(str(records[-1].msg), '{} Hello, world!')
+ self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
+ adapter2.log(logging.WARNING, 'Goodbye {}.', 'world')
+ self.assertEqual(str(records[-1].msg), '{} Goodbye world.')
+ self.assertEqual(records[-1].funcName, 'test_nested_styled_adapter')
+
+ def test_find_caller_with_stacklevel(self):
+ the_level = 1
+ trigger = self.adapter.warning
+
+ def innermost():
+ trigger('test', stacklevel=the_level)
+
+ def inner():
+ innermost()
+
+ def outer():
+ inner()
+
+ records = self.recording.records
+ outer()
+ self.assertEqual(records[-1].funcName, 'innermost')
+ lineno = records[-1].lineno
+ the_level += 1
+ outer()
+ self.assertEqual(records[-1].funcName, 'inner')
+ self.assertGreater(records[-1].lineno, lineno)
+ lineno = records[-1].lineno
+ the_level += 1
+ outer()
+ self.assertEqual(records[-1].funcName, 'outer')
+ self.assertGreater(records[-1].lineno, lineno)
+ lineno = records[-1].lineno
+ the_level += 1
+ outer()
+ self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
+ self.assertGreater(records[-1].lineno, lineno)
+
def test_extra_in_records(self):
self.adapter = logging.LoggerAdapter(logger=self.logger,
extra={'foo': '1'})
@@ -5569,6 +5620,30 @@ class LoggerAdapterTest(unittest.TestCase):
self.assertEqual(record.foo, '2')
+class PrefixAdapter(logging.LoggerAdapter):
+ prefix = 'Adapter'
+
+ def process(self, msg, kwargs):
+ return f"{self.prefix} {msg}", kwargs
+
+
+class Message:
+ def __init__(self, fmt, args):
+ self.fmt = fmt
+ self.args = args
+
+ def __str__(self):
+ return self.fmt.format(*self.args)
+
+
+class StyleAdapter(logging.LoggerAdapter):
+ def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
+ if self.isEnabledFor(level):
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.log(level, Message(msg, args), **kwargs,
+ stacklevel=stacklevel+1)
+
+
class LoggerTest(BaseTest, AssertErrorMessage):
def setUp(self):
diff --git a/Misc/NEWS.d/next/Documentation/2024-02-12-12-26-17.gh-issue-115233.aug6r9.rst b/Misc/NEWS.d/next/Documentation/2024-02-12-12-26-17.gh-issue-115233.aug6r9.rst
new file mode 100644
index 0000000..f37f94d
--- /dev/null
+++ b/Misc/NEWS.d/next/Documentation/2024-02-12-12-26-17.gh-issue-115233.aug6r9.rst
@@ -0,0 +1 @@
+Fix an example for :class:`~logging.LoggerAdapter` in the Logging Cookbook.