Logging bugfix (#18465)

* use mp Manager to handle logging queues

A Python bug (https://github.com/python/cpython/issues/91555) was preventing logs from the embeddings maintainer process from printing. The bug is fixed in Python 3.14, but a viable workaround is to use the multiprocessing Manager, which better manages mp queues and causes the logging to work correctly.

* consolidate

* fix typing
This commit is contained in:
Josh Hawkins 2025-05-29 10:02:17 -05:00 committed by GitHub
parent 9b7d4d0a8f
commit c11ca42fb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 15 additions and 5 deletions

View File

@ -44,6 +44,7 @@ from frigate.embeddings import EmbeddingsContext, manage_embeddings
from frigate.events.audio import AudioProcessor from frigate.events.audio import AudioProcessor
from frigate.events.cleanup import EventCleanup from frigate.events.cleanup import EventCleanup
from frigate.events.maintainer import EventProcessor from frigate.events.maintainer import EventProcessor
from frigate.log import _stop_logging
from frigate.models import ( from frigate.models import (
Event, Event,
Export, Export,
@ -771,4 +772,7 @@ class FrigateApp:
shm.close() shm.close()
shm.unlink() shm.unlink()
# exit the mp Manager process
_stop_logging()
os._exit(os.EX_OK) os._exit(os.EX_OK)

View File

@ -1,3 +1,4 @@
# In log.py
import atexit import atexit
import logging import logging
import multiprocessing as mp import multiprocessing as mp
@ -6,6 +7,7 @@ import sys
import threading import threading
from collections import deque from collections import deque
from logging.handlers import QueueHandler, QueueListener from logging.handlers import QueueHandler, QueueListener
from queue import Queue
from typing import Deque, Optional from typing import Deque, Optional
from frigate.util.builtin import clean_camera_user_pass from frigate.util.builtin import clean_camera_user_pass
@ -32,12 +34,14 @@ LOG_HANDLER.addFilter(
) )
log_listener: Optional[QueueListener] = None log_listener: Optional[QueueListener] = None
log_queue: Optional[Queue] = None
manager = None
def setup_logging() -> None: def setup_logging() -> None:
global log_listener global log_listener, log_queue, manager
manager = mp.Manager()
log_queue: mp.Queue = mp.Queue() log_queue = manager.Queue()
log_listener = QueueListener(log_queue, LOG_HANDLER, respect_handler_level=True) log_listener = QueueListener(log_queue, LOG_HANDLER, respect_handler_level=True)
atexit.register(_stop_logging) atexit.register(_stop_logging)
@ -53,11 +57,13 @@ def setup_logging() -> None:
def _stop_logging() -> None: def _stop_logging() -> None:
global log_listener global log_listener, manager
if log_listener is not None: if log_listener is not None:
log_listener.stop() log_listener.stop()
log_listener = None log_listener = None
if manager is not None:
manager.shutdown()
manager = None
# When a multiprocessing.Process exits, python tries to flush stdout and stderr. However, if the # When a multiprocessing.Process exits, python tries to flush stdout and stderr. However, if the