]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-115233: Fix an example in the Logging Cookbook (GH-115325)
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 12 Feb 2024 16:24:45 +0000 (18:24 +0200)
committerGitHub <noreply@github.com>
Mon, 12 Feb 2024 16:24:45 +0000 (18:24 +0200)
Also add more tests for LoggerAdapter.

Also support stacklevel in LoggerAdapter._log().

Doc/howto/logging-cookbook.rst
Lib/logging/__init__.py
Lib/test/test_logging.py
Misc/NEWS.d/next/Documentation/2024-02-12-12-26-17.gh-issue-115233.aug6r9.rst [new file with mode: 0644]

index 80147e31fcbae115171abacc9b3a637d534d0ae4..f7d885ec88483d7d6e7f1a1113c17cf5ed0dd405 100644 (file)
@@ -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
index 684b58d5548f91f5d762eff33644a9e3830f422b..fcec9e76b9866187c17d081bed6b45a5f43910d8 100644 (file)
@@ -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):
index 888523227c2ac43026908f9f312c4dbe86fc5865..cf09bad4c9187bc63d7797893ea5e60ebe4b0e47 100644 (file)
@@ -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 (file)
index 0000000..f37f94d
--- /dev/null
@@ -0,0 +1 @@
+Fix an example for :class:`~logging.LoggerAdapter` in the Logging Cookbook.