summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAvram Lubkin <git_hub@avram.us>2017-07-30 09:36:33 (GMT)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2017-07-30 09:36:33 (GMT)
commit78c18a9b9a1445f7c755929917a790ba02b4a5e0 (patch)
tree4632a83a3fd2ac4d9902b3556b4cb5381d85426d
parentcaa1280d1ee5f828f346b585169a7592371d3faa (diff)
downloadcpython-78c18a9b9a1445f7c755929917a790ba02b4a5e0.zip
cpython-78c18a9b9a1445f7c755929917a790ba02b4a5e0.tar.gz
cpython-78c18a9b9a1445f7c755929917a790ba02b4a5e0.tar.bz2
bpo-30962: Added caching to Logger.isEnabledFor() (GH-2752)
-rw-r--r--Lib/logging/__init__.py30
-rw-r--r--Lib/test/test_logging.py60
2 files changed, 86 insertions, 4 deletions
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 64e24ee..f9bfb79 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1244,6 +1244,19 @@ class Manager(object):
alogger.parent = c.parent
c.parent = alogger
+ def _clear_cache(self):
+ """
+ Clear the cache for all loggers in loggerDict
+ Called when level changes are made
+ """
+
+ _acquireLock()
+ for logger in self.loggerDict.values():
+ if isinstance(logger, Logger):
+ logger._cache.clear()
+ self.root._cache.clear()
+ _releaseLock()
+
#---------------------------------------------------------------------------
# Logger classes and functions
#---------------------------------------------------------------------------
@@ -1274,12 +1287,14 @@ class Logger(Filterer):
self.propagate = True
self.handlers = []
self.disabled = False
+ self._cache = {}
def setLevel(self, level):
"""
Set the logging level of this logger. level must be an int or a str.
"""
self.level = _checkLevel(level)
+ self.manager._clear_cache()
def debug(self, msg, *args, **kwargs):
"""
@@ -1543,9 +1558,17 @@ class Logger(Filterer):
"""
Is this logger enabled for level 'level'?
"""
- if self.manager.disable >= level:
- return False
- return level >= self.getEffectiveLevel()
+ try:
+ return self._cache[level]
+ except KeyError:
+ _acquireLock()
+ if self.manager.disable >= level:
+ is_enabled = self._cache[level] = False
+ else:
+ is_enabled = self._cache[level] = level >= self.getEffectiveLevel()
+ _releaseLock()
+
+ return is_enabled
def getChild(self, suffix):
"""
@@ -1910,6 +1933,7 @@ def disable(level=CRITICAL):
Disable all logging calls of severity 'level' and below.
"""
root.manager.disable = level
+ root.manager._clear_cache()
def shutdown(handlerList=_handlerList):
"""
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 880f4e8..8884562 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -135,7 +135,9 @@ class BaseTest(unittest.TestCase):
logging._handlers.clear()
logging._handlers.update(self.saved_handlers)
logging._handlerList[:] = self.saved_handler_list
- loggerDict = logging.getLogger().manager.loggerDict
+ manager = logging.getLogger().manager
+ manager.disable = 0
+ loggerDict = manager.loggerDict
loggerDict.clear()
loggerDict.update(self.saved_loggers)
logger_states = self.logger_states
@@ -4094,6 +4096,62 @@ class LoggerTest(BaseTest):
unpickled = pickle.loads(s)
self.assertIs(unpickled, logger)
+ def test_caching(self):
+ root = self.root_logger
+ logger1 = logging.getLogger("abc")
+ logger2 = logging.getLogger("abc.def")
+
+ # Set root logger level and ensure cache is empty
+ root.setLevel(logging.ERROR)
+ self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
+ self.assertEqual(logger2._cache, {})
+
+ # Ensure cache is populated and calls are consistent
+ self.assertTrue(logger2.isEnabledFor(logging.ERROR))
+ self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
+ self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
+ self.assertEqual(root._cache, {})
+ self.assertTrue(logger2.isEnabledFor(logging.ERROR))
+
+ # Ensure root cache gets populated
+ self.assertEqual(root._cache, {})
+ self.assertTrue(root.isEnabledFor(logging.ERROR))
+ self.assertEqual(root._cache, {logging.ERROR: True})
+
+ # Set parent logger level and ensure caches are emptied
+ logger1.setLevel(logging.CRITICAL)
+ self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
+ self.assertEqual(logger2._cache, {})
+
+ # Ensure logger2 uses parent logger's effective level
+ self.assertFalse(logger2.isEnabledFor(logging.ERROR))
+
+ # Set level to NOTSET and ensure caches are empty
+ logger2.setLevel(logging.NOTSET)
+ self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
+ self.assertEqual(logger2._cache, {})
+ self.assertEqual(logger1._cache, {})
+ self.assertEqual(root._cache, {})
+
+ # Verify logger2 follows parent and not root
+ self.assertFalse(logger2.isEnabledFor(logging.ERROR))
+ self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
+ self.assertFalse(logger1.isEnabledFor(logging.ERROR))
+ self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
+ self.assertTrue(root.isEnabledFor(logging.ERROR))
+
+ # Disable logging in manager and ensure caches are clear
+ logging.disable()
+ self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
+ self.assertEqual(logger2._cache, {})
+ self.assertEqual(logger1._cache, {})
+ self.assertEqual(root._cache, {})
+
+ # Ensure no loggers are enabled
+ self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
+ self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
+ self.assertFalse(root.isEnabledFor(logging.CRITICAL))
+
class BaseFileTest(BaseTest):
"Base class for handler tests that write log files"