mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Make logging code self-contained (#13785)
* Make logging code self-contained. Rewrite logging code to use python's builting QueueListener, effectively moving the logging process into a thread of the Frigate app. Also, wrap this behaviour in a easy-to-use context manager to encourage some consistency. * Fixed typing errors * Remove todo note from log filter Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com> * Do not access log record's msg directly * Clear all root handlers before starting app --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
parent
f7eaace7ae
commit
1c24f0054a
@ -1,17 +1,28 @@
|
||||
import faulthandler
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from flask import cli
|
||||
|
||||
from frigate.app import FrigateApp
|
||||
|
||||
|
||||
def main() -> None:
|
||||
faulthandler.enable()
|
||||
|
||||
threading.current_thread().name = "frigate"
|
||||
# Clear all existing handlers.
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
handlers=[],
|
||||
force=True,
|
||||
)
|
||||
|
||||
threading.current_thread().name = "frigate"
|
||||
cli.show_server_banner = lambda *x: None
|
||||
|
||||
if __name__ == "__main__":
|
||||
frigate_app = FrigateApp()
|
||||
# Run the main application.
|
||||
FrigateApp().start()
|
||||
|
||||
frigate_app.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -43,7 +43,7 @@ from frigate.events.audio import listen_to_audio
|
||||
from frigate.events.cleanup import EventCleanup
|
||||
from frigate.events.external import ExternalEventProcessor
|
||||
from frigate.events.maintainer import EventProcessor
|
||||
from frigate.log import log_process, root_configurer
|
||||
from frigate.log import log_thread
|
||||
from frigate.models import (
|
||||
Event,
|
||||
Export,
|
||||
@ -113,15 +113,6 @@ class FrigateApp:
|
||||
else:
|
||||
logger.debug(f"Skipping directory: {d}")
|
||||
|
||||
def init_logger(self) -> None:
|
||||
self.log_process = mp.Process(
|
||||
target=log_process, args=(self.log_queue,), name="log_process"
|
||||
)
|
||||
self.log_process.daemon = True
|
||||
self.log_process.start()
|
||||
self.processes["logger"] = self.log_process.pid or 0
|
||||
root_configurer(self.log_queue)
|
||||
|
||||
def init_config(self) -> None:
|
||||
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
|
||||
|
||||
@ -667,6 +658,7 @@ class FrigateApp:
|
||||
logger.info("********************************************************")
|
||||
logger.info("********************************************************")
|
||||
|
||||
@log_thread()
|
||||
def start(self) -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="Frigate",
|
||||
@ -675,7 +667,6 @@ class FrigateApp:
|
||||
parser.add_argument("--validate-config", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
self.init_logger()
|
||||
logger.info(f"Starting Frigate ({VERSION})")
|
||||
|
||||
try:
|
||||
@ -702,13 +693,11 @@ class FrigateApp:
|
||||
print("*************************************************************")
|
||||
print("*** End Config Validation Errors ***")
|
||||
print("*************************************************************")
|
||||
self.log_process.terminate()
|
||||
sys.exit(1)
|
||||
if args.validate_config:
|
||||
print("*************************************************************")
|
||||
print("*** Your config file is valid. ***")
|
||||
print("*************************************************************")
|
||||
self.log_process.terminate()
|
||||
sys.exit(0)
|
||||
self.set_environment_vars()
|
||||
self.set_log_levels()
|
||||
@ -725,7 +714,6 @@ class FrigateApp:
|
||||
self.init_dispatcher()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.log_process.terminate()
|
||||
sys.exit(1)
|
||||
self.start_detectors()
|
||||
self.start_video_output_processor()
|
||||
@ -848,7 +836,4 @@ class FrigateApp:
|
||||
shm.close()
|
||||
shm.unlink()
|
||||
|
||||
self.log_process.terminate()
|
||||
self.log_process.join()
|
||||
|
||||
os._exit(os.EX_OK)
|
||||
|
@ -1,71 +1,71 @@
|
||||
# adapted from https://medium.com/@jonathonbao/python3-logging-with-multiprocessing-f51f460b8778
|
||||
import atexit
|
||||
import logging
|
||||
import multiprocessing as mp
|
||||
import os
|
||||
import queue
|
||||
import signal
|
||||
import threading
|
||||
from collections import deque
|
||||
from logging import handlers
|
||||
from multiprocessing import Queue
|
||||
from types import FrameType
|
||||
from contextlib import AbstractContextManager, ContextDecorator
|
||||
from logging.handlers import QueueHandler, QueueListener
|
||||
from types import TracebackType
|
||||
from typing import Deque, Optional
|
||||
|
||||
from setproctitle import setproctitle
|
||||
from typing_extensions import Self
|
||||
|
||||
from frigate.util.builtin import clean_camera_user_pass
|
||||
|
||||
|
||||
def listener_configurer() -> None:
|
||||
root = logging.getLogger()
|
||||
|
||||
if root.hasHandlers():
|
||||
root.handlers.clear()
|
||||
|
||||
console_handler = logging.StreamHandler()
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] %(name)-30s %(levelname)-8s: %(message)s", "%Y-%m-%d %H:%M:%S"
|
||||
LOG_HANDLER = logging.StreamHandler()
|
||||
LOG_HANDLER.setFormatter(
|
||||
logging.Formatter(
|
||||
"[%(asctime)s] %(name)-30s %(levelname)-8s: %(message)s",
|
||||
"%Y-%m-%d %H:%M:%S",
|
||||
)
|
||||
)
|
||||
|
||||
LOG_HANDLER.addFilter(
|
||||
lambda record: not record.getMessage().startswith(
|
||||
"You are using a scalar distance function"
|
||||
)
|
||||
)
|
||||
console_handler.setFormatter(formatter)
|
||||
root.addHandler(console_handler)
|
||||
root.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def root_configurer(queue: Queue) -> None:
|
||||
h = handlers.QueueHandler(queue)
|
||||
root = logging.getLogger()
|
||||
class log_thread(AbstractContextManager, ContextDecorator):
|
||||
def __init__(self, *, handler: logging.Handler = LOG_HANDLER):
|
||||
super().__init__()
|
||||
|
||||
if root.hasHandlers():
|
||||
root.handlers.clear()
|
||||
self._handler = handler
|
||||
|
||||
root.addHandler(h)
|
||||
root.setLevel(logging.INFO)
|
||||
log_queue: mp.Queue = mp.Queue()
|
||||
self._queue_handler = QueueHandler(log_queue)
|
||||
|
||||
self._log_listener = QueueListener(
|
||||
log_queue, self._handler, respect_handler_level=True
|
||||
)
|
||||
|
||||
def log_process(log_queue: Queue) -> None:
|
||||
threading.current_thread().name = "logger"
|
||||
setproctitle("frigate.logger")
|
||||
listener_configurer()
|
||||
@property
|
||||
def handler(self) -> logging.Handler:
|
||||
return self._handler
|
||||
|
||||
stop_event = mp.Event()
|
||||
def _stop_thread(self) -> None:
|
||||
self._log_listener.stop()
|
||||
|
||||
def receiveSignal(signalNumber: int, frame: Optional[FrameType]) -> None:
|
||||
stop_event.set()
|
||||
def __enter__(self) -> Self:
|
||||
logging.getLogger().addHandler(self._queue_handler)
|
||||
|
||||
signal.signal(signal.SIGTERM, receiveSignal)
|
||||
signal.signal(signal.SIGINT, receiveSignal)
|
||||
atexit.register(self._stop_thread)
|
||||
self._log_listener.start()
|
||||
|
||||
while True:
|
||||
try:
|
||||
record = log_queue.get(block=True, timeout=1.0)
|
||||
except queue.Empty:
|
||||
if stop_event.is_set():
|
||||
break
|
||||
continue
|
||||
if record.msg.startswith("You are using a scalar distance function"):
|
||||
continue
|
||||
logger = logging.getLogger(record.name)
|
||||
logger.handle(record)
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[type[BaseException]],
|
||||
exc_info: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
logging.getLogger().removeHandler(self._queue_handler)
|
||||
|
||||
atexit.unregister(self._stop_thread)
|
||||
self._stop_thread()
|
||||
|
||||
|
||||
# based on https://codereview.stackexchange.com/a/17959
|
||||
|
@ -28,8 +28,7 @@ from frigate.video import ( # noqa: E402
|
||||
start_or_restart_ffmpeg,
|
||||
)
|
||||
|
||||
logging.basicConfig()
|
||||
logging.root.setLevel(logging.DEBUG)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user