mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
New stats module, refactor stats generation out of http module.
StatsEmitter thread to send stats to MQTT every 60 seconds by default, optional stats_interval config value. New service stats attribute, containing uptime in seconds and version.
This commit is contained in:
parent
a9afa303a2
commit
76403bba8e
@ -20,6 +20,7 @@ from frigate.models import Event
|
|||||||
from frigate.mqtt import create_mqtt_client
|
from frigate.mqtt import create_mqtt_client
|
||||||
from frigate.object_processing import TrackedObjectProcessor
|
from frigate.object_processing import TrackedObjectProcessor
|
||||||
from frigate.record import RecordingMaintainer
|
from frigate.record import RecordingMaintainer
|
||||||
|
from frigate.stats import StatsEmitter, stats_init
|
||||||
from frigate.video import capture_camera, track_camera
|
from frigate.video import capture_camera, track_camera
|
||||||
from frigate.watchdog import FrigateWatchdog
|
from frigate.watchdog import FrigateWatchdog
|
||||||
from frigate.zeroconf import broadcast_zeroconf
|
from frigate.zeroconf import broadcast_zeroconf
|
||||||
@ -115,8 +116,11 @@ class FrigateApp():
|
|||||||
self.db.bind(models)
|
self.db.bind(models)
|
||||||
self.db.create_tables(models, safe=True)
|
self.db.create_tables(models, safe=True)
|
||||||
|
|
||||||
|
def init_stats(self):
|
||||||
|
self.stats_tracking = stats_init(self.camera_metrics, self.detectors)
|
||||||
|
|
||||||
def init_web_server(self):
|
def init_web_server(self):
|
||||||
self.flask_app = create_app(self.config, self.db, self.camera_metrics, self.detectors, self.detected_frames_processor)
|
self.flask_app = create_app(self.config, self.db, self.stats_tracking, self.detected_frames_processor)
|
||||||
|
|
||||||
def init_mqtt(self):
|
def init_mqtt(self):
|
||||||
self.mqtt_client = create_mqtt_client(self.config)
|
self.mqtt_client = create_mqtt_client(self.config)
|
||||||
@ -173,6 +177,10 @@ class FrigateApp():
|
|||||||
self.recording_maintainer = RecordingMaintainer(self.config, self.stop_event)
|
self.recording_maintainer = RecordingMaintainer(self.config, self.stop_event)
|
||||||
self.recording_maintainer.start()
|
self.recording_maintainer.start()
|
||||||
|
|
||||||
|
def start_stats_emitter(self):
|
||||||
|
self.stats_emitter = StatsEmitter(self.config, self.stats_tracking, self.mqtt_client, self.config.mqtt.topic_prefix, self.stop_event)
|
||||||
|
self.stats_emitter.start()
|
||||||
|
|
||||||
def start_watchdog(self):
|
def start_watchdog(self):
|
||||||
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
self.frigate_watchdog = FrigateWatchdog(self.detectors, self.stop_event)
|
||||||
self.frigate_watchdog.start()
|
self.frigate_watchdog.start()
|
||||||
@ -200,10 +208,12 @@ class FrigateApp():
|
|||||||
self.start_detected_frames_processor()
|
self.start_detected_frames_processor()
|
||||||
self.start_camera_processors()
|
self.start_camera_processors()
|
||||||
self.start_camera_capture_processes()
|
self.start_camera_capture_processes()
|
||||||
|
self.init_stats()
|
||||||
self.init_web_server()
|
self.init_web_server()
|
||||||
self.start_event_processor()
|
self.start_event_processor()
|
||||||
self.start_event_cleanup()
|
self.start_event_cleanup()
|
||||||
self.start_recording_maintainer()
|
self.start_recording_maintainer()
|
||||||
|
self.start_stats_emitter()
|
||||||
self.start_watchdog()
|
self.start_watchdog()
|
||||||
# self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
|
# self.zeroconf = broadcast_zeroconf(self.config.mqtt.client_id)
|
||||||
|
|
||||||
@ -224,6 +234,7 @@ class FrigateApp():
|
|||||||
self.event_processor.join()
|
self.event_processor.join()
|
||||||
self.event_cleanup.join()
|
self.event_cleanup.join()
|
||||||
self.recording_maintainer.join()
|
self.recording_maintainer.join()
|
||||||
|
self.stats_emitter.join()
|
||||||
self.frigate_watchdog.join()
|
self.frigate_watchdog.join()
|
||||||
|
|
||||||
for detector in self.detectors.values():
|
for detector in self.detectors.values():
|
||||||
|
@ -37,6 +37,7 @@ MQTT_SCHEMA = vol.Schema(
|
|||||||
vol.Optional('port', default=1883): int,
|
vol.Optional('port', default=1883): int,
|
||||||
vol.Optional('topic_prefix', default='frigate'): str,
|
vol.Optional('topic_prefix', default='frigate'): str,
|
||||||
vol.Optional('client_id', default='frigate'): str,
|
vol.Optional('client_id', default='frigate'): str,
|
||||||
|
vol.Optional('stats_interval', default=60): int,
|
||||||
'user': str,
|
'user': str,
|
||||||
'password': str
|
'password': str
|
||||||
}
|
}
|
||||||
@ -324,6 +325,7 @@ class MqttConfig():
|
|||||||
self._client_id = config['client_id']
|
self._client_id = config['client_id']
|
||||||
self._user = config.get('user')
|
self._user = config.get('user')
|
||||||
self._password = config.get('password')
|
self._password = config.get('password')
|
||||||
|
self._stats_interval = config.get('stats_interval')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host(self):
|
def host(self):
|
||||||
@ -349,13 +351,18 @@ class MqttConfig():
|
|||||||
def password(self):
|
def password(self):
|
||||||
return self._password
|
return self._password
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stats_interval(self):
|
||||||
|
return self._stats_interval
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
'host': self.host,
|
'host': self.host,
|
||||||
'port': self.port,
|
'port': self.port,
|
||||||
'topic_prefix': self.topic_prefix,
|
'topic_prefix': self.topic_prefix,
|
||||||
'client_id': self.client_id,
|
'client_id': self.client_id,
|
||||||
'user': self.user
|
'user': self.user,
|
||||||
|
'stats_interval': self.stats_interval
|
||||||
}
|
}
|
||||||
|
|
||||||
class CameraInput():
|
class CameraInput():
|
||||||
|
@ -13,6 +13,7 @@ from peewee import SqliteDatabase, operator, fn, DoesNotExist
|
|||||||
from playhouse.shortcuts import model_to_dict
|
from playhouse.shortcuts import model_to_dict
|
||||||
|
|
||||||
from frigate.models import Event
|
from frigate.models import Event
|
||||||
|
from frigate.stats import stats_snapshot
|
||||||
from frigate.util import calculate_region
|
from frigate.util import calculate_region
|
||||||
from frigate.version import VERSION
|
from frigate.version import VERSION
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
bp = Blueprint('frigate', __name__)
|
bp = Blueprint('frigate', __name__)
|
||||||
|
|
||||||
def create_app(frigate_config, database: SqliteDatabase, camera_metrics, detectors, detected_frames_processor):
|
def create_app(frigate_config, database: SqliteDatabase, stats_tracking, detected_frames_processor):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
@ -33,8 +34,7 @@ def create_app(frigate_config, database: SqliteDatabase, camera_metrics, detecto
|
|||||||
database.close()
|
database.close()
|
||||||
|
|
||||||
app.frigate_config = frigate_config
|
app.frigate_config = frigate_config
|
||||||
app.camera_metrics = camera_metrics
|
app.stats_tracking = stats_tracking
|
||||||
app.detectors = detectors
|
|
||||||
app.detected_frames_processor = detected_frames_processor
|
app.detected_frames_processor = detected_frames_processor
|
||||||
|
|
||||||
app.register_blueprint(bp)
|
app.register_blueprint(bp)
|
||||||
@ -152,31 +152,7 @@ def version():
|
|||||||
|
|
||||||
@bp.route('/stats')
|
@bp.route('/stats')
|
||||||
def stats():
|
def stats():
|
||||||
camera_metrics = current_app.camera_metrics
|
stats = stats_snapshot(current_app.stats_tracking)
|
||||||
stats = {}
|
|
||||||
|
|
||||||
total_detection_fps = 0
|
|
||||||
|
|
||||||
for name, camera_stats in camera_metrics.items():
|
|
||||||
total_detection_fps += camera_stats['detection_fps'].value
|
|
||||||
stats[name] = {
|
|
||||||
'camera_fps': round(camera_stats['camera_fps'].value, 2),
|
|
||||||
'process_fps': round(camera_stats['process_fps'].value, 2),
|
|
||||||
'skipped_fps': round(camera_stats['skipped_fps'].value, 2),
|
|
||||||
'detection_fps': round(camera_stats['detection_fps'].value, 2),
|
|
||||||
'pid': camera_stats['process'].pid,
|
|
||||||
'capture_pid': camera_stats['capture_process'].pid
|
|
||||||
}
|
|
||||||
|
|
||||||
stats['detectors'] = {}
|
|
||||||
for name, detector in current_app.detectors.items():
|
|
||||||
stats['detectors'][name] = {
|
|
||||||
'inference_speed': round(detector.avg_inference_speed.value*1000, 2),
|
|
||||||
'detection_start': detector.detection_start.value,
|
|
||||||
'pid': detector.detect_process.pid
|
|
||||||
}
|
|
||||||
stats['detection_fps'] = round(total_detection_fps, 2)
|
|
||||||
|
|
||||||
return jsonify(stats)
|
return jsonify(stats)
|
||||||
|
|
||||||
@bp.route('/<camera_name>/<label>/best.jpg')
|
@bp.route('/<camera_name>/<label>/best.jpg')
|
||||||
|
70
frigate/stats.py
Normal file
70
frigate/stats.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
from frigate.config import FrigateConfig
|
||||||
|
from frigate.version import VERSION
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def stats_init(camera_metrics, detectors):
|
||||||
|
stats_tracking = {
|
||||||
|
'camera_metrics': camera_metrics,
|
||||||
|
'detectors': detectors,
|
||||||
|
'started': int(time.time())
|
||||||
|
}
|
||||||
|
return stats_tracking
|
||||||
|
|
||||||
|
def stats_snapshot(stats_tracking):
|
||||||
|
camera_metrics = stats_tracking['camera_metrics']
|
||||||
|
stats = {}
|
||||||
|
|
||||||
|
total_detection_fps = 0
|
||||||
|
|
||||||
|
for name, camera_stats in camera_metrics.items():
|
||||||
|
total_detection_fps += camera_stats['detection_fps'].value
|
||||||
|
stats[name] = {
|
||||||
|
'camera_fps': round(camera_stats['camera_fps'].value, 2),
|
||||||
|
'process_fps': round(camera_stats['process_fps'].value, 2),
|
||||||
|
'skipped_fps': round(camera_stats['skipped_fps'].value, 2),
|
||||||
|
'detection_fps': round(camera_stats['detection_fps'].value, 2),
|
||||||
|
'pid': camera_stats['process'].pid,
|
||||||
|
'capture_pid': camera_stats['capture_process'].pid
|
||||||
|
}
|
||||||
|
|
||||||
|
stats['detectors'] = {}
|
||||||
|
for name, detector in stats_tracking["detectors"].items():
|
||||||
|
stats['detectors'][name] = {
|
||||||
|
'inference_speed': round(detector.avg_inference_speed.value * 1000, 2),
|
||||||
|
'detection_start': detector.detection_start.value,
|
||||||
|
'pid': detector.detect_process.pid
|
||||||
|
}
|
||||||
|
stats['detection_fps'] = round(total_detection_fps, 2)
|
||||||
|
|
||||||
|
stats['service'] = {
|
||||||
|
'uptime': (int(time.time()) - stats_tracking['started']),
|
||||||
|
'version': VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats
|
||||||
|
|
||||||
|
class StatsEmitter(threading.Thread):
|
||||||
|
def __init__(self, config: FrigateConfig, stats_tracking, mqtt_client, topic_prefix, stop_event):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
self.name = 'frigate_stats_emitter'
|
||||||
|
self.config = config
|
||||||
|
self.stats_tracking = stats_tracking
|
||||||
|
self.mqtt_client = mqtt_client
|
||||||
|
self.topic_prefix = topic_prefix
|
||||||
|
self.stop_event = stop_event
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
time.sleep(10)
|
||||||
|
while True:
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
logger.info(f"Exiting watchdog...")
|
||||||
|
break
|
||||||
|
stats = stats_snapshot(self.stats_tracking)
|
||||||
|
self.mqtt_client.publish(f"{self.topic_prefix}/stats", json.dumps(stats), retain=False)
|
||||||
|
time.sleep(self.config.mqtt.stats_interval)
|
Loading…
Reference in New Issue
Block a user