diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 15d5ac28179183..01e94143268089 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1356,6 +1356,8 @@ def __init__(self, rootnode): self.disable = 0 self.emittedNoHandlerWarning = False self.loggerDict = {} + # Loggers whose isEnabledFor cache may be populated; see _clear_cache. + self._cached_loggers = weakref.WeakSet() self.loggerClass = None self.logRecordFactory = None @@ -1471,9 +1473,12 @@ def _clear_cache(self): """ with _lock: - for logger in self.loggerDict.values(): - if isinstance(logger, Logger): + # Only loggers that have populated a cache are visited, so + # setLevel() costs O(loggers with cached results), not O(loggers). + for logger in list(self._cached_loggers): + if self.loggerDict.get(logger.name) is logger: logger._cache.clear() + self._cached_loggers.discard(logger) self.root._cache.clear() #--------------------------------------------------------------------------- @@ -1799,6 +1804,7 @@ def isEnabledFor(self, level): return self._cache[level] except KeyError: with _lock: + self.manager._cached_loggers.add(self) if self.manager.disable >= level: is_enabled = self._cache[level] = False else: diff --git a/Misc/NEWS.d/next/Library/2026-10-29-20-30-00.gh-issue-160936.lgCach.rst b/Misc/NEWS.d/next/Library/2026-10-29-20-30-00.gh-issue-160936.lgCach.rst new file mode 100644 index 00000000000000..b079fb01160095 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-29-20-30-00.gh-issue-160936.lgCach.rst @@ -0,0 +1 @@ +Speed up :meth:`logging.Logger.setLevel` and :func:`logging.disable` when there are many loggers: only loggers that have cached results are now cleared, instead of every logger.