[Python-checkins] gh-76913: Add "merge extras" feature to LoggerAdapter (GH-107292)
vsajip
webhook-mailer at python.org
Tue Aug 15 03:23:58 EDT 2023
https://github.com/python/cpython/commit/a482e5bf0022f85424a6308529a9ad51f1bfbb71
commit: a482e5bf0022f85424a6308529a9ad51f1bfbb71
branch: main
author: Romuald Brunet <romuald at chivil.com>
committer: vsajip <vinay_sajip at yahoo.co.uk>
date: 2023-08-15T08:23:54+01:00
summary:
gh-76913: Add "merge extras" feature to LoggerAdapter (GH-107292)
files:
A Misc/NEWS.d/next/Library/2023-08-14-17-15-59.gh-issue-76913.LLD0rT.rst
M Doc/library/logging.rst
M Lib/logging/__init__.py
M Lib/test/test_logging.py
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index b582c918df023..49e870e9e2479 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -1002,10 +1002,14 @@ LoggerAdapter Objects
information into logging calls. For a usage example, see the section on
:ref:`adding contextual information to your logging output <context-info>`.
-.. class:: LoggerAdapter(logger, extra)
+.. class:: LoggerAdapter(logger, extra, merge_extra=False)
Returns an instance of :class:`LoggerAdapter` initialized with an
- underlying :class:`Logger` instance and a dict-like object.
+ underlying :class:`Logger` instance, a dict-like object (*extra*), and a
+ boolean (*merge_extra*) indicating whether or not the *extra* argument of
+ individual log calls should be merged with the :class:`LoggerAdapter` extra.
+ The default behavior is to ignore the *extra* argument of individual log
+ calls and only use the one of the :class:`LoggerAdapter` instance
.. method:: process(msg, kwargs)
@@ -1037,6 +1041,9 @@ interchangeably.
Remove the undocumented ``warn()`` method which was an alias to the
``warning()`` method.
+.. versionchanged:: 3.13
+ The *merge_extra* argument was added.
+
Thread Safety
-------------
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 527fc5c631730..2d228e563094c 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1879,7 +1879,7 @@ class LoggerAdapter(object):
information in logging output.
"""
- def __init__(self, logger, extra=None):
+ def __init__(self, logger, extra=None, merge_extra=False):
"""
Initialize the adapter with a logger and a dict-like object which
provides contextual information. This constructor signature allows
@@ -1889,9 +1889,20 @@ def __init__(self, logger, extra=None):
following example:
adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
+
+ By default, LoggerAdapter objects will drop the "extra" argument
+ passed on the individual log calls to use its own instead.
+
+ Initializing it with merge_extra=True will instead merge both
+ maps when logging, the individual call extra taking precedence
+ over the LoggerAdapter instance extra
+
+ .. versionchanged:: 3.13
+ The *merge_extra* argument was added.
"""
self.logger = logger
self.extra = extra
+ self.merge_extra = merge_extra
def process(self, msg, kwargs):
"""
@@ -1903,7 +1914,10 @@ def process(self, msg, kwargs):
Normally, you'll only need to override this one method in a
LoggerAdapter subclass for your specific needs.
"""
- kwargs["extra"] = self.extra
+ if self.merge_extra and "extra" in kwargs:
+ kwargs["extra"] = {**self.extra, **kwargs["extra"]}
+ else:
+ kwargs["extra"] = self.extra
return msg, kwargs
#
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index def976fbe96ba..f26846f9663e5 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -5433,6 +5433,46 @@ def process(self, msg, kwargs):
self.assertIs(adapter.manager, orig_manager)
self.assertIs(self.logger.manager, orig_manager)
+ 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'))
+
+ def test_extra_merged(self):
+ self.adapter = logging.LoggerAdapter(logger=self.logger,
+ extra={'foo': '1'},
+ merge_extra=True)
+
+ self.adapter.critical('foo and bar should be here', extra={'bar': '2'})
+ self.assertEqual(len(self.recording.records), 1)
+ record = self.recording.records[0]
+ self.assertTrue(hasattr(record, 'foo'))
+ self.assertTrue(hasattr(record, 'bar'))
+ self.assertEqual(record.foo, '1')
+ self.assertEqual(record.bar, '2')
+
+ def test_extra_merged_log_call_has_precedence(self):
+ self.adapter = logging.LoggerAdapter(logger=self.logger,
+ extra={'foo': '1'},
+ merge_extra=True)
+
+ self.adapter.critical('foo shall be min', extra={'foo': '2'})
+ self.assertEqual(len(self.recording.records), 1)
+ record = self.recording.records[0]
+ self.assertTrue(hasattr(record, 'foo'))
+ self.assertEqual(record.foo, '2')
+
class LoggerTest(BaseTest, AssertErrorMessage):
diff --git a/Misc/NEWS.d/next/Library/2023-08-14-17-15-59.gh-issue-76913.LLD0rT.rst b/Misc/NEWS.d/next/Library/2023-08-14-17-15-59.gh-issue-76913.LLD0rT.rst
new file mode 100644
index 0000000000000..5f9a84e714ae2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-08-14-17-15-59.gh-issue-76913.LLD0rT.rst
@@ -0,0 +1 @@
+Add *merge_extra* parameter/feature to :class:`logging.LoggerAdapter`
More information about the Python-checkins
mailing list