diff --git a/homeassistant/util/logging.py b/homeassistant/util/logging.py index 1e516742bfe..d5dfab7da6c 100644 --- a/homeassistant/util/logging.py +++ b/homeassistant/util/logging.py @@ -29,16 +29,22 @@ class HomeAssistantQueueListener(logging.handlers.QueueListener): LOG_COUNTS_RESET_INTERVAL = 300 MAX_LOGS_COUNT = 200 + EXCLUDED_LOG_COUNT_MODULES = [ + "homeassistant.components.automation", + "homeassistant.components.script", + "homeassistant.setup", + "homeassistant.util.logging", + ] + _last_reset: float _log_counts: dict[str, int] - _warned_modules: set[str] def __init__( self, queue: SimpleQueue[logging.Handler], *handlers: logging.Handler ) -> None: """Initialize the handler.""" super().__init__(queue, *handlers) - self._warned_modules = set() + self._module_log_count_skip_flags: dict[str, bool] = {} self._reset_counters(time.time()) @override @@ -53,7 +59,11 @@ class HomeAssistantQueueListener(logging.handlers.QueueListener): self._reset_counters(record.created) module_name = record.name - if module_name == __name__ or module_name in self._warned_modules: + + if skip_flag := self._module_log_count_skip_flags.get(module_name): + return + + if skip_flag is None and self._update_skip_flags(module_name): return self._log_counts[module_name] += 1 @@ -66,13 +76,20 @@ class HomeAssistantQueueListener(logging.handlers.QueueListener): module_name, module_count, ) - self._warned_modules.add(module_name) + self._module_log_count_skip_flags[module_name] = True def _reset_counters(self, time_sec: float) -> None: _LOGGER.debug("Resetting log counters") self._last_reset = time_sec self._log_counts = defaultdict(int) + def _update_skip_flags(self, module_name: str) -> bool: + excluded = any( + module_name.startswith(prefix) for prefix in self.EXCLUDED_LOG_COUNT_MODULES + ) + self._module_log_count_skip_flags[module_name] = excluded + return excluded + class HomeAssistantQueueHandler(logging.handlers.QueueHandler): """Process the log in another thread.""" diff --git a/tests/util/test_logging.py b/tests/util/test_logging.py index d213a68d7f2..ba473ee0c58 100644 --- a/tests/util/test_logging.py +++ b/tests/util/test_logging.py @@ -160,6 +160,10 @@ async def test_catch_log_exception_catches_and_logs() -> None: @patch("homeassistant.util.logging.HomeAssistantQueueListener.MAX_LOGS_COUNT", 5) +@patch( + "homeassistant.util.logging.HomeAssistantQueueListener.EXCLUDED_LOG_COUNT_MODULES", + ["excluded"], +) @pytest.mark.parametrize( ( "logger1_count", @@ -182,6 +186,7 @@ async def test_noisy_loggers( logging_util.async_activate_log_queue_handler(hass) logger1 = logging.getLogger("noisy1") logger2 = logging.getLogger("noisy2.module") + logger_excluded = logging.getLogger("excluded.module") for _ in range(logger1_count): logger1.info("This is a log") @@ -189,6 +194,9 @@ async def test_noisy_loggers( for _ in range(logger2_count): logger2.info("This is another log") + for _ in range(logging_util.HomeAssistantQueueListener.MAX_LOGS_COUNT + 1): + logger_excluded.info("This log should not trigger a warning") + await empty_log_queue() assert ( @@ -203,6 +211,33 @@ async def test_noisy_loggers( ) == logger2_expected_notices ) + # Ensure that the excluded module did not trigger a warning + assert ( + caplog.text.count("is logging too frequently") + == logger1_expected_notices + logger2_expected_notices + ) + + # close the handler so the queue thread stops + logging.root.handlers[0].close() + + +@patch("homeassistant.util.logging.HomeAssistantQueueListener.MAX_LOGS_COUNT", 1) +async def test_noisy_loggers_ignores_self( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test that the noisy loggers warning does not trigger a warning for its own module.""" + + logging_util.async_activate_log_queue_handler(hass) + logger1 = logging.getLogger("noisy_module1") + logger2 = logging.getLogger("noisy_module2") + logger3 = logging.getLogger("noisy_module3") + + logger1.info("This is a log") + logger2.info("This is a log") + logger3.info("This is a log") + + await empty_log_queue() + assert caplog.text.count("logging too frequently") == 3 # close the handler so the queue thread stops logging.root.handlers[0].close()