[Update] テレメトリ機能の改善と不要なコードの削除
This commit is contained in:
@@ -10,7 +10,6 @@ from concurrent.futures import Future
|
||||
# Allow running as a script for quick verification.
|
||||
try:
|
||||
from .state import TelemetryState
|
||||
from .heartbeat import HeartbeatManager
|
||||
from .core import TelemetryCore
|
||||
except ImportError:
|
||||
import os
|
||||
@@ -18,7 +17,6 @@ except ImportError:
|
||||
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
|
||||
from models.telemetry.state import TelemetryState
|
||||
from models.telemetry.heartbeat import HeartbeatManager
|
||||
from models.telemetry.core import TelemetryCore
|
||||
|
||||
|
||||
@@ -36,9 +34,9 @@ class Telemetry:
|
||||
return
|
||||
self.state = TelemetryState()
|
||||
self.core = TelemetryCore(self.state)
|
||||
self.heartbeat = HeartbeatManager(self.state, self.core, self._schedule_async_with_loop)
|
||||
self._loop = None
|
||||
self._loop_thread = None
|
||||
self._init_called = False # init()が呼ばれたかを追跡(重複初期化防止)
|
||||
self._initialized = True
|
||||
|
||||
def _start_event_loop(self):
|
||||
@@ -96,12 +94,20 @@ class Telemetry:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _schedule_async_with_loop(self, coro):
|
||||
"""ループ取得とスケジューリングのヘルパー(heartbeat用)"""
|
||||
return self._schedule_async(coro)
|
||||
|
||||
def init(self, enabled: bool, app_version: str = "1.0.0"):
|
||||
"""テレメトリ初期化(同期インターフェース)"""
|
||||
"""テレメトリ初期化(同期インターフェース)
|
||||
|
||||
重要:このメソッドは冪等です。複数回呼ばれても安全です。
|
||||
既に初期化済みの場合は、有効/無効の状態のみを更新します。
|
||||
これにより、設定変更時などに誤ってapp_startedイベントが重複送信される問題を防ぎます。
|
||||
"""
|
||||
# 既に初期化済みの場合は、状態の更新のみ
|
||||
if self._init_called:
|
||||
self.state.set_enabled(enabled)
|
||||
return
|
||||
|
||||
# 初回初期化
|
||||
self._init_called = True
|
||||
self.state.set_enabled(enabled)
|
||||
if enabled:
|
||||
self._start_event_loop()
|
||||
@@ -111,23 +117,18 @@ class Telemetry:
|
||||
"""非同期初期化処理"""
|
||||
await self.core.start(app_version=app_version)
|
||||
await self.core.send_event("app_started")
|
||||
self.heartbeat.start()
|
||||
|
||||
def shutdown(self):
|
||||
"""テレメトリ終了(同期インターフェース)
|
||||
|
||||
重要:Tauri sidecar環境では、このメソッド実行後にプロセス終了が発生します。
|
||||
app_closed イベントが確実に送信されるように、以下の手順を実行します:
|
||||
1. heartbeat スレッド停止
|
||||
2. app_closed イベント送信を同期待機
|
||||
3. Aptabase クライアント停止(フラッシュ含む)
|
||||
4. イベントループ完全停止を待機
|
||||
1. app_closed イベント送信を同期待機
|
||||
2. Aptabase クライアント停止(フラッシュ含む)
|
||||
3. イベントループ完全停止を待機
|
||||
"""
|
||||
try:
|
||||
# Step 1: Heartbeat 停止(5分待機中の送信を防ぐ)
|
||||
self.heartbeat.stop()
|
||||
|
||||
# Step 2-3: app_closed 送信とクライアント停止を同期待機
|
||||
# app_closed 送信とクライアント停止を同期待機
|
||||
if self.state.is_enabled():
|
||||
try:
|
||||
# _run_async で最大5秒間待機(Aptabase のフラッシュ含む)
|
||||
@@ -135,7 +136,7 @@ class Telemetry:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Step 4: イベントループを完全停止(フラッシュ確認を待つ)
|
||||
# イベントループを完全停止(フラッシュ確認を待つ)
|
||||
# sidecar 終了前に確実に完了させるため、タイムアウトを長めに設定
|
||||
self._stop_event_loop(timeout=5.0)
|
||||
except Exception:
|
||||
@@ -144,6 +145,7 @@ class Telemetry:
|
||||
finally:
|
||||
# 状態をリセット
|
||||
self.state.reset()
|
||||
self._init_called = False # 初期化フラグもリセット
|
||||
|
||||
async def _shutdown_async(self):
|
||||
"""非同期終了処理"""
|
||||
@@ -160,7 +162,6 @@ class Telemetry:
|
||||
"""コア機能イベント送信(同期インターフェース)"""
|
||||
if not self.state.is_enabled():
|
||||
return
|
||||
self.state.touch_activity()
|
||||
if self.core.is_duplicate_core_feature(feature):
|
||||
return
|
||||
self._schedule_async(self._track_core_feature_async(feature))
|
||||
@@ -170,11 +171,6 @@ class Telemetry:
|
||||
await self.core.send_event("core_feature", {"feature": feature})
|
||||
self.state.record_feature_sent(feature)
|
||||
|
||||
def touch_activity(self):
|
||||
"""アクティビティ時刻更新"""
|
||||
if self.state.is_enabled():
|
||||
self.state.touch_activity()
|
||||
|
||||
def is_enabled(self) -> bool:
|
||||
"""有効状態確認"""
|
||||
return self.state.is_enabled()
|
||||
@@ -190,7 +186,6 @@ if __name__ == "__main__":
|
||||
telemetry.init(enabled=True)
|
||||
telemetry.track("debug_test", {"message": "telemetry main demo"})
|
||||
telemetry.track_core_feature("text_input")
|
||||
telemetry.touch_activity()
|
||||
print("state:", telemetry.get_state())
|
||||
|
||||
# イベント送信完了を待つ
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Aptabase SDK ラッパー(非同期版)
|
||||
"""
|
||||
import logging
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
# Aptabase SDK のインポート
|
||||
@@ -11,10 +12,12 @@ except ImportError:
|
||||
|
||||
|
||||
class AptabaseWrapper:
|
||||
APP_KEY = "A-US-2082730845"
|
||||
APP_KEY = "A-US-6044063021"
|
||||
|
||||
def __init__(self):
|
||||
self.client = None
|
||||
# Suppress noisy logs from the Aptabase SDK (only CRITICAL allowed)
|
||||
logging.getLogger("aptabase").setLevel(logging.CRITICAL)
|
||||
|
||||
async def start(self, app_version: str = "1.0.0"):
|
||||
"""Aptabase クライアント開始"""
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
"""
|
||||
Heartbeat スレッド管理
|
||||
- 5分間隔でアクティブ確認
|
||||
- 操作なしで5分経過したら送信停止
|
||||
"""
|
||||
from datetime import datetime
|
||||
from threading import Thread, Event
|
||||
import time
|
||||
|
||||
|
||||
class HeartbeatManager:
|
||||
INTERVAL = 300 # 5 minutes
|
||||
TIMEOUT = 300 # 5 minutes
|
||||
|
||||
def __init__(self, state, core, schedule_async):
|
||||
self.state = state
|
||||
self.core = core
|
||||
self.schedule_async = schedule_async
|
||||
self.thread = None
|
||||
self._stop_event = Event()
|
||||
|
||||
def start(self):
|
||||
"""Heartbeat スレッド開始"""
|
||||
if self.thread is not None and self.thread.is_alive():
|
||||
return
|
||||
self._stop_event.clear()
|
||||
self.thread = Thread(target=self._run, daemon=True, name="telemetry_heartbeat")
|
||||
self.thread.start()
|
||||
|
||||
def stop(self):
|
||||
"""Heartbeat スレッド停止"""
|
||||
self._stop_event.set()
|
||||
if self.thread is not None:
|
||||
self.thread.join(timeout=1.0)
|
||||
self.thread = None
|
||||
|
||||
def _run(self):
|
||||
"""Heartbeat ループ"""
|
||||
while not self._stop_event.is_set():
|
||||
time.sleep(self.INTERVAL)
|
||||
|
||||
if not self.state.is_enabled():
|
||||
continue
|
||||
|
||||
last_activity = self.state.get_last_activity()
|
||||
if last_activity is None:
|
||||
continue
|
||||
|
||||
elapsed = (datetime.now() - last_activity).total_seconds()
|
||||
if elapsed < self.TIMEOUT:
|
||||
# アクティブなら heartbeat 送信(非同期スケジュール)
|
||||
if self.core.client is not None:
|
||||
self.schedule_async(self.core.send_event("session_heartbeat"))
|
||||
@@ -11,7 +11,6 @@ from threading import Lock
|
||||
class TelemetryState:
|
||||
def __init__(self):
|
||||
self._enabled = True # デフォルト有効
|
||||
self._last_activity = None
|
||||
self._session_features_sent = set()
|
||||
self._lock = Lock()
|
||||
|
||||
@@ -27,16 +26,6 @@ class TelemetryState:
|
||||
with self._lock:
|
||||
return self._enabled
|
||||
|
||||
def touch_activity(self):
|
||||
"""最終操作時刻更新"""
|
||||
with self._lock:
|
||||
self._last_activity = datetime.now()
|
||||
|
||||
def get_last_activity(self):
|
||||
"""最終操作時刻取得"""
|
||||
with self._lock:
|
||||
return self._last_activity
|
||||
|
||||
def record_feature_sent(self, feature: str):
|
||||
"""送信済み機能を記録"""
|
||||
with self._lock:
|
||||
@@ -50,7 +39,6 @@ class TelemetryState:
|
||||
def reset(self):
|
||||
"""状態をリセット"""
|
||||
with self._lock:
|
||||
self._last_activity = None
|
||||
self._session_features_sent.clear()
|
||||
|
||||
def get_debug_info(self) -> dict:
|
||||
@@ -58,6 +46,5 @@ class TelemetryState:
|
||||
with self._lock:
|
||||
return {
|
||||
"enabled": self._enabled,
|
||||
"last_activity": self._last_activity.isoformat() if self._last_activity else None,
|
||||
"session_features_sent": list(self._session_features_sent),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user