From 0e27fe65304a15b901ba01b95941943c3c81de21 Mon Sep 17 00:00:00 2001 From: kirill kulakov Date: Thu, 15 Jan 2026 21:51:10 -0600 Subject: [PATCH] Prevent log spam from unexpected cache files Addresses PR review feedback: Add deduplication to prevent warning messages from being logged repeatedly for the same unexpected file in the cache directory. Each unexpected filename is only logged once per RecordingMaintainer instance lifecycle. Also adds test to verify warning is only emitted once per filename. --- frigate/record/maintainer.py | 9 ++++++-- frigate/test/test_maintainer.py | 40 +++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/frigate/record/maintainer.py b/frigate/record/maintainer.py index 33f721aa3..3dd65909b 100644 --- a/frigate/record/maintainer.py +++ b/frigate/record/maintainer.py @@ -97,6 +97,7 @@ class RecordingMaintainer(threading.Thread): self.object_recordings_info: dict[str, list] = defaultdict(list) self.audio_recordings_info: dict[str, list] = defaultdict(list) self.end_time_cache: dict[str, Tuple[datetime.datetime, float]] = {} + self.unexpected_cache_files_seen: set[str] = set() async def move_files(self) -> None: cache_files = [ @@ -115,7 +116,9 @@ class RecordingMaintainer(threading.Thread): try: camera, date = basename.rsplit("@", maxsplit=1) except ValueError: - logger.warning(f"Skipping unexpected file in cache: {cache}") + if cache not in self.unexpected_cache_files_seen: + logger.warning(f"Skipping unexpected file in cache: {cache}") + self.unexpected_cache_files_seen.add(cache) continue start_time = datetime.datetime.strptime( @@ -172,7 +175,9 @@ class RecordingMaintainer(threading.Thread): try: camera, date = basename.rsplit("@", maxsplit=1) except ValueError: - logger.warning(f"Skipping unexpected file in cache: {cache}") + if cache not in self.unexpected_cache_files_seen: + logger.warning(f"Skipping unexpected file in cache: {cache}") + self.unexpected_cache_files_seen.add(cache) continue # important that start_time is utc because recordings are stored and compared in utc diff --git a/frigate/test/test_maintainer.py b/frigate/test/test_maintainer.py index f3556a0ee..1d036150a 100644 --- a/frigate/test/test_maintainer.py +++ b/frigate/test/test_maintainer.py @@ -32,18 +32,34 @@ class TestMaintainer(unittest.IsolatedAsyncioTestCase): with patch('os.listdir', return_value=files): with patch('os.path.isfile', return_value=True): with patch('frigate.record.maintainer.psutil.process_iter', return_value=[]): - # Mock validate_and_move_segment to avoid further logic - maintainer.validate_and_move_segment = MagicMock() - - try: - await maintainer.move_files() - except ValueError as e: - if "not enough values to unpack" in str(e): - self.fail("move_files() crashed on bad filename!") - raise e - except Exception: - # Ignore other errors (like DB connection) as we only care about the unpack crash - pass + with patch('frigate.record.maintainer.logger.warning') as warn: + # Mock validate_and_move_segment to avoid further logic + maintainer.validate_and_move_segment = MagicMock() + + try: + await maintainer.move_files() + except ValueError as e: + if "not enough values to unpack" in str(e): + self.fail("move_files() crashed on bad filename!") + raise e + except Exception: + # Ignore other errors (like DB connection) as we only care about the unpack crash + pass + + # The bad filename is encountered in multiple loops, but should only warn once. + matching = [ + c + for c in warn.call_args_list + if c.args + and isinstance(c.args[0], str) + and "Skipping unexpected file in cache: bad_filename.mp4" + in c.args[0] + ] + self.assertEqual( + 1, + len(matching), + f"Expected a single warning for bad filename, got {len(matching)}", + ) if __name__ == '__main__': unittest.main()