mirror of
https://github.com/python/cpython.git
synced 2025-08-17 23:31:09 +00:00
[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 commit225856ef3e
) (cherry picked from commit91822018ee
) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
d732134232
commit
21edde17b3
4 changed files with 106 additions and 23 deletions
|
@ -1744,13 +1744,11 @@ to the above, as in the following example::
|
||||||
return self.fmt.format(*self.args)
|
return self.fmt.format(*self.args)
|
||||||
|
|
||||||
class StyleAdapter(logging.LoggerAdapter):
|
class StyleAdapter(logging.LoggerAdapter):
|
||||||
def __init__(self, logger, extra=None):
|
def log(self, level, msg, /, *args, stacklevel=1, **kwargs):
|
||||||
super().__init__(logger, extra or {})
|
|
||||||
|
|
||||||
def log(self, level, msg, /, *args, **kwargs):
|
|
||||||
if self.isEnabledFor(level):
|
if self.isEnabledFor(level):
|
||||||
msg, kwargs = self.process(msg, kwargs)
|
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__))
|
logger = StyleAdapter(logging.getLogger(__name__))
|
||||||
|
|
||||||
|
@ -1762,7 +1760,7 @@ to the above, as in the following example::
|
||||||
main()
|
main()
|
||||||
|
|
||||||
The above script should log the message ``Hello, world!`` when run with
|
The above script should log the message ``Hello, world!`` when run with
|
||||||
Python 3.2 or later.
|
Python 3.8 or later.
|
||||||
|
|
||||||
|
|
||||||
.. currentmodule:: logging
|
.. currentmodule:: logging
|
||||||
|
|
|
@ -1910,18 +1910,11 @@ class LoggerAdapter(object):
|
||||||
"""
|
"""
|
||||||
return self.logger.hasHandlers()
|
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.
|
Low-level log implementation, proxied to allow nested logger adapters.
|
||||||
"""
|
"""
|
||||||
return self.logger._log(
|
return self.logger._log(level, msg, args, **kwargs)
|
||||||
level,
|
|
||||||
msg,
|
|
||||||
args,
|
|
||||||
exc_info=exc_info,
|
|
||||||
extra=extra,
|
|
||||||
stack_info=stack_info,
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def manager(self):
|
def manager(self):
|
||||||
|
|
|
@ -5075,6 +5075,7 @@ class LoggerAdapterTest(unittest.TestCase):
|
||||||
self.assertEqual(record.levelno, logging.CRITICAL)
|
self.assertEqual(record.levelno, logging.CRITICAL)
|
||||||
self.assertEqual(record.msg, msg)
|
self.assertEqual(record.msg, msg)
|
||||||
self.assertEqual(record.args, (self.recording,))
|
self.assertEqual(record.args, (self.recording,))
|
||||||
|
self.assertEqual(record.funcName, 'test_critical')
|
||||||
|
|
||||||
def test_is_enabled_for(self):
|
def test_is_enabled_for(self):
|
||||||
old_disable = self.adapter.logger.manager.disable
|
old_disable = self.adapter.logger.manager.disable
|
||||||
|
@ -5093,15 +5094,9 @@ class LoggerAdapterTest(unittest.TestCase):
|
||||||
self.assertFalse(self.adapter.hasHandlers())
|
self.assertFalse(self.adapter.hasHandlers())
|
||||||
|
|
||||||
def test_nested(self):
|
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.'
|
msg = 'Adapters can be nested, yo.'
|
||||||
adapter = Adapter(logger=self.logger, extra=None)
|
adapter = PrefixAdapter(logger=self.logger, extra=None)
|
||||||
adapter_adapter = Adapter(logger=adapter, extra=None)
|
adapter_adapter = PrefixAdapter(logger=adapter, extra=None)
|
||||||
adapter_adapter.prefix = 'AdapterAdapter'
|
adapter_adapter.prefix = 'AdapterAdapter'
|
||||||
self.assertEqual(repr(adapter), repr(adapter_adapter))
|
self.assertEqual(repr(adapter), repr(adapter_adapter))
|
||||||
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
|
adapter_adapter.log(logging.CRITICAL, msg, self.recording)
|
||||||
|
@ -5110,6 +5105,7 @@ class LoggerAdapterTest(unittest.TestCase):
|
||||||
self.assertEqual(record.levelno, logging.CRITICAL)
|
self.assertEqual(record.levelno, logging.CRITICAL)
|
||||||
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
|
self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
|
||||||
self.assertEqual(record.args, (self.recording,))
|
self.assertEqual(record.args, (self.recording,))
|
||||||
|
self.assertEqual(record.funcName, 'test_nested')
|
||||||
orig_manager = adapter_adapter.manager
|
orig_manager = adapter_adapter.manager
|
||||||
self.assertIs(adapter.manager, orig_manager)
|
self.assertIs(adapter.manager, orig_manager)
|
||||||
self.assertIs(self.logger.manager, orig_manager)
|
self.assertIs(self.logger.manager, orig_manager)
|
||||||
|
@ -5125,6 +5121,101 @@ class LoggerAdapterTest(unittest.TestCase):
|
||||||
self.assertIs(adapter.manager, orig_manager)
|
self.assertIs(adapter.manager, orig_manager)
|
||||||
self.assertIs(self.logger.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'})
|
||||||
|
|
||||||
|
self.adapter.critical('foo should be here')
|
||||||
|
self.assertEqual(len(self.recording.records), 1)
|
||||||
|
record = self.recording.records[0]
|
||||||
|
self.assertTrue(hasattr(record, 'foo'))
|
||||||
|
self.assertEqual(record.foo, '1')
|
||||||
|
|
||||||
|
def test_extra_not_merged_by_default(self):
|
||||||
|
self.adapter.critical('foo should NOT be here', extra={'foo': 'nope'})
|
||||||
|
self.assertEqual(len(self.recording.records), 1)
|
||||||
|
record = self.recording.records[0]
|
||||||
|
self.assertFalse(hasattr(record, 'foo'))
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
class LoggerTest(BaseTest, AssertErrorMessage):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix an example for :class:`~logging.LoggerAdapter` in the Logging Cookbook.
|
Loading…
Add table
Add a link
Reference in a new issue