Commit 78c18a9b authored by Avram Lubkin's avatar Avram Lubkin Committed by Vinay Sajip

bpo-30962: Added caching to Logger.isEnabledFor() (GH-2752)

parent caa1280d
...@@ -1244,6 +1244,19 @@ class Manager(object): ...@@ -1244,6 +1244,19 @@ class Manager(object):
alogger.parent = c.parent alogger.parent = c.parent
c.parent = alogger 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 # Logger classes and functions
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
...@@ -1274,12 +1287,14 @@ class Logger(Filterer): ...@@ -1274,12 +1287,14 @@ class Logger(Filterer):
self.propagate = True self.propagate = True
self.handlers = [] self.handlers = []
self.disabled = False self.disabled = False
self._cache = {}
def setLevel(self, level): def setLevel(self, level):
""" """
Set the logging level of this logger. level must be an int or a str. Set the logging level of this logger. level must be an int or a str.
""" """
self.level = _checkLevel(level) self.level = _checkLevel(level)
self.manager._clear_cache()
def debug(self, msg, *args, **kwargs): def debug(self, msg, *args, **kwargs):
""" """
...@@ -1543,9 +1558,17 @@ class Logger(Filterer): ...@@ -1543,9 +1558,17 @@ class Logger(Filterer):
""" """
Is this logger enabled for level 'level'? Is this logger enabled for level 'level'?
""" """
try:
return self._cache[level]
except KeyError:
_acquireLock()
if self.manager.disable >= level: if self.manager.disable >= level:
return False is_enabled = self._cache[level] = False
return level >= self.getEffectiveLevel() else:
is_enabled = self._cache[level] = level >= self.getEffectiveLevel()
_releaseLock()
return is_enabled
def getChild(self, suffix): def getChild(self, suffix):
""" """
...@@ -1910,6 +1933,7 @@ def disable(level=CRITICAL): ...@@ -1910,6 +1933,7 @@ def disable(level=CRITICAL):
Disable all logging calls of severity 'level' and below. Disable all logging calls of severity 'level' and below.
""" """
root.manager.disable = level root.manager.disable = level
root.manager._clear_cache()
def shutdown(handlerList=_handlerList): def shutdown(handlerList=_handlerList):
""" """
......
...@@ -135,7 +135,9 @@ class BaseTest(unittest.TestCase): ...@@ -135,7 +135,9 @@ class BaseTest(unittest.TestCase):
logging._handlers.clear() logging._handlers.clear()
logging._handlers.update(self.saved_handlers) logging._handlers.update(self.saved_handlers)
logging._handlerList[:] = self.saved_handler_list logging._handlerList[:] = self.saved_handler_list
loggerDict = logging.getLogger().manager.loggerDict manager = logging.getLogger().manager
manager.disable = 0
loggerDict = manager.loggerDict
loggerDict.clear() loggerDict.clear()
loggerDict.update(self.saved_loggers) loggerDict.update(self.saved_loggers)
logger_states = self.logger_states logger_states = self.logger_states
...@@ -4094,6 +4096,62 @@ class LoggerTest(BaseTest): ...@@ -4094,6 +4096,62 @@ class LoggerTest(BaseTest):
unpickled = pickle.loads(s) unpickled = pickle.loads(s)
self.assertIs(unpickled, logger) 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): class BaseFileTest(BaseTest):
"Base class for handler tests that write log files" "Base class for handler tests that write log files"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment