Files
VRCT/src-python/docs/details/watchdog.md
2025-10-14 07:28:03 +09:00

670 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# watchdog.py - 軽量監視システム
## 概要
タイムアウトベースの軽量監視(ウォッチドッグ)システムです。定期的な"餌やり"feedにより正常動作を確認し、指定時間内に餌やりがない場合にコールバック関数を実行する単純で効果的な監視機構を提供します。
## 主要機能
### タイムアウト監視
- 最後の餌やり時刻からの経過時間監視
- 設定可能なタイムアウト閾値
- タイムアウト時の自動コールバック実行
### 柔軟な実行モード
- 単発チェック(手動呼び出し)
- バックグラウンドスレッド実行
- カスタムチェック間隔設定
### 防御的設計
- コールバック例外の隔離処理
- スレッドセーフな制御機構
- 適切なリソース管理
## クラス構造
### Watchdog クラス
```python
class Watchdog:
def __init__(self, timeout: int = 60, interval: int = 20) -> None:
self.timeout: int # タイムアウト秒数
self.interval: int # チェック間隔秒数
self.last_feed_time: float # 最後の餌やり時刻
self.callback: Optional[Callable] # タイムアウト時コールバック
self._thread: Optional[Thread] # バックグラウンドスレッド
self._stop_event: Optional[Event] # 停止イベント
```
軽量ウォッチドッグの中核クラス
#### パラメータ
- **timeout**: 餌やりなしでタイムアウトするまでの秒数
- **interval**: 監視チェックの推奨間隔秒数
## 主要メソッド
### 基本制御
```python
def feed(self) -> None
```
ウォッチドッグに餌やりを行い、タイマーをリセット
```python
def setCallback(self, callback: Callable[[], None]) -> None
```
タイムアウト時に実行するコールバック関数を設定
#### パラメータ
- **callback**: 引数なしの呼び出し可能オブジェクト
### 監視実行
```python
def start(self) -> None
```
単発のウォッチドッグチェックを実行し、間隔秒数だけスリープ
```python
def start_in_thread(self, daemon: bool = True) -> None
```
バックグラウンドスレッドでウォッチドッグを開始
#### パラメータ
- **daemon**: デーモンスレッドとして実行するかのフラグ
```python
def stop(self, timeout: Optional[float] = None) -> None
```
バックグラウンドスレッドを停止
#### パラメータ
- **timeout**: スレッド終了待機のタイムアウト秒数
## 使用方法
### 基本的な監視システム
```python
from models.watchdog.watchdog import Watchdog
import time
def on_timeout():
"""タイムアウト時の処理"""
print("警告: システムの応答がありません!")
# ログ出力、アラート送信、復旧処理等
# ウォッチドッグの初期化
watchdog = Watchdog(timeout=30, interval=10) # 30秒でタイムアウト、10秒間隔
watchdog.setCallback(on_timeout)
# バックグラウンドで監視開始
watchdog.start_in_thread(daemon=True)
# メインプロセスのシミュレーション
for i in range(10):
print(f"処理中... {i}")
# 正常な処理では定期的に餌やり
if i % 3 == 0: # 3回に1回餌やり
watchdog.feed()
print("ウォッチドッグに餌やりしました")
time.sleep(5)
# 監視停止
watchdog.stop()
```
### 手動チェックモード
```python
# 手動でウォッチドッグをチェック
def manual_monitoring_example():
watchdog = Watchdog(timeout=60, interval=5)
def system_failure_handler():
print("システム障害を検出しました")
# 復旧処理、通知等
watchdog.setCallback(system_failure_handler)
# メインループ内で定期チェック
while True:
# 何らかの重要な処理
process_critical_work()
# 処理が正常なら餌やり
if is_system_healthy():
watchdog.feed()
# 監視チェック実行5秒間隔でスリープ
watchdog.start()
def process_critical_work():
"""重要な処理のシミュレーション"""
time.sleep(2)
def is_system_healthy():
"""システム正常性チェックのシミュレーション"""
import random
return random.random() > 0.2 # 80%の確率で正常
# manual_monitoring_example()
```
### プロセス監視システム
```python
class ProcessMonitor:
"""外部プロセス監視システム"""
def __init__(self, process_name, check_interval=30):
self.process_name = process_name
self.watchdog = Watchdog(timeout=60, interval=check_interval)
self.watchdog.setCallback(self.on_process_timeout)
self.monitoring = False
def on_process_timeout(self):
"""プロセス応答タイムアウト時の処理"""
print(f"警告: プロセス {self.process_name} が応答しません")
# プロセス存在確認
if self.is_process_running():
print("プロセスは実行中ですが応答なし。再起動を試行します。")
self.restart_process()
else:
print("プロセスが停止しています。再起動します。")
self.start_process()
def is_process_running(self):
"""プロセス実行状態確認"""
import psutil
for proc in psutil.process_iter(['name']):
if proc.info['name'] == self.process_name:
return True
return False
def start_process(self):
"""プロセス起動"""
print(f"プロセス {self.process_name} を起動中...")
# 実際の起動処理
def restart_process(self):
"""プロセス再起動"""
print(f"プロセス {self.process_name} を再起動中...")
# 実際の再起動処理
def feed_watchdog(self):
"""外部から呼び出される餌やりメソッド"""
self.watchdog.feed()
def start_monitoring(self):
"""監視開始"""
self.monitoring = True
self.watchdog.start_in_thread(daemon=True)
print(f"プロセス {self.process_name} の監視を開始しました")
def stop_monitoring(self):
"""監視停止"""
self.monitoring = False
self.watchdog.stop()
print(f"プロセス {self.process_name} の監視を停止しました")
# 使用例
vrchat_monitor = ProcessMonitor("VRChat.exe", check_interval=15)
vrchat_monitor.start_monitoring()
# VRChatプロセスが正常に動作している時の餌やり
# 実際にはVRChatからのOSC通信等で判定
for _ in range(20):
time.sleep(10)
if vrchat_monitor.is_process_running():
vrchat_monitor.feed_watchdog()
print("VRChat正常動作確認")
vrchat_monitor.stop_monitoring()
```
### ネットワーク監視システム
```python
class NetworkWatchdog:
"""ネットワーク接続監視"""
def __init__(self, target_host="8.8.8.8", timeout=45):
self.target_host = target_host
self.watchdog = Watchdog(timeout=timeout, interval=15)
self.watchdog.setCallback(self.on_network_timeout)
self.last_ping_success = True
def on_network_timeout(self):
"""ネットワークタイムアウト処理"""
print("ネットワーク接続に問題があります")
# 複数ホストでの確認
test_hosts = ["8.8.8.8", "1.1.1.1", "google.com"]
for host in test_hosts:
if self.ping_host(host):
print(f"{host} への接続は正常です")
self.watchdog.feed() # 1つでも成功したら復旧とみなす
return
print("すべてのホストへの接続に失敗。ネットワーク設定を確認してください。")
self.handle_network_failure()
def ping_host(self, host):
"""ホストへのping確認"""
import subprocess
import platform
# プラットフォームに応じたpingコマンド
if platform.system().lower() == "windows":
cmd = ["ping", "-n", "1", "-w", "3000", host]
else:
cmd = ["ping", "-c", "1", "-W", "3", host]
try:
result = subprocess.run(cmd, capture_output=True, timeout=5)
return result.returncode == 0
except (subprocess.TimeoutExpired, Exception):
return False
def handle_network_failure(self):
"""ネットワーク障害時の処理"""
print("ネットワーク障害対応処理を実行中...")
# DNS設定リセット、ネットワークアダプター再起動等
def check_network_continuously(self):
"""継続的なネットワーク監視"""
self.watchdog.start_in_thread(daemon=True)
while True:
if self.ping_host(self.target_host):
if not self.last_ping_success:
print("ネットワーク接続が復旧しました")
self.watchdog.feed()
self.last_ping_success = True
else:
if self.last_ping_success:
print("ネットワーク接続に問題が発生しました")
self.last_ping_success = False
time.sleep(10)
# 使用例
network_monitor = NetworkWatchdog(target_host="google.com", timeout=60)
# バックグラウンドでネットワーク監視
import threading
monitor_thread = threading.Thread(target=network_monitor.check_network_continuously, daemon=True)
monitor_thread.start()
# メインプログラムの実行
print("ネットワーク監視システム開始...")
time.sleep(120) # 2分間監視
network_monitor.watchdog.stop()
```
### システムリソース監視
```python
class SystemResourceWatchdog:
"""システムリソース監視"""
def __init__(self):
self.cpu_watchdog = Watchdog(timeout=300, interval=30) # CPU使用率監視
self.memory_watchdog = Watchdog(timeout=180, interval=20) # メモリ監視
self.cpu_watchdog.setCallback(self.on_cpu_overload)
self.memory_watchdog.setCallback(self.on_memory_pressure)
self.cpu_threshold = 80.0 # CPU使用率閾値%
self.memory_threshold = 85.0 # メモリ使用率閾値(%
def on_cpu_overload(self):
"""CPU過負荷時の処理"""
print("警告: CPU使用率が長時間高い状態です")
self.optimize_cpu_usage()
def on_memory_pressure(self):
"""メモリ圧迫時の処理"""
print("警告: メモリ使用率が危険なレベルです")
self.free_memory()
def get_cpu_usage(self):
"""CPU使用率取得"""
import psutil
return psutil.cpu_percent(interval=1)
def get_memory_usage(self):
"""メモリ使用率取得"""
import psutil
return psutil.virtual_memory().percent
def optimize_cpu_usage(self):
"""CPU使用率最適化"""
print("CPU最適化処理を実行中...")
# 低優先度プロセスの特定・制限
# 不要なバックグラウンドタスクの停止等
def free_memory(self):
"""メモリ解放処理"""
print("メモリ解放処理を実行中...")
# ガベージコレクション実行
import gc
gc.collect()
# キャッシュクリア等
def monitor_resources(self):
"""リソース監視メインループ"""
self.cpu_watchdog.start_in_thread(daemon=True)
self.memory_watchdog.start_in_thread(daemon=True)
while True:
# CPU使用率チェック
cpu_usage = self.get_cpu_usage()
if cpu_usage < self.cpu_threshold:
self.cpu_watchdog.feed()
# メモリ使用率チェック
memory_usage = self.get_memory_usage()
if memory_usage < self.memory_threshold:
self.memory_watchdog.feed()
print(f"CPU: {cpu_usage:.1f}%, メモリ: {memory_usage:.1f}%")
time.sleep(5)
def stop_monitoring(self):
"""監視停止"""
self.cpu_watchdog.stop()
self.memory_watchdog.stop()
# 使用例
resource_monitor = SystemResourceWatchdog()
# リソース監視開始
import threading
resource_thread = threading.Thread(target=resource_monitor.monitor_resources, daemon=True)
resource_thread.start()
# しばらく監視
time.sleep(60)
resource_monitor.stop_monitoring()
```
## 高度な使用パターン
### 多段階監視システム
```python
class MultilevelWatchdog:
"""多段階警告レベル対応ウォッチドッグ"""
def __init__(self):
# 異なるタイムアウトレベル
self.warning_watchdog = Watchdog(timeout=30, interval=10) # 警告レベル
self.critical_watchdog = Watchdog(timeout=60, interval=15) # 危険レベル
self.emergency_watchdog = Watchdog(timeout=120, interval=20) # 緊急レベル
# 各レベルのコールバック設定
self.warning_watchdog.setCallback(self.on_warning)
self.critical_watchdog.setCallback(self.on_critical)
self.emergency_watchdog.setCallback(self.on_emergency)
self.alert_level = "normal"
def on_warning(self):
"""警告レベルのコールバック"""
self.alert_level = "warning"
print("⚠️ 警告: システムの応答が遅くなっています")
# 軽度の対応処理
def on_critical(self):
"""危険レベルのコールバック"""
self.alert_level = "critical"
print("🔴 危険: システムの重大な問題を検出")
# 中程度の復旧処理
def on_emergency(self):
"""緊急レベルのコールバック"""
self.alert_level = "emergency"
print("🚨 緊急: システムが完全に応答停止")
# 強制復旧・再起動処理
def feed_all(self):
"""すべてのウォッチドッグに餌やり"""
self.warning_watchdog.feed()
self.critical_watchdog.feed()
self.emergency_watchdog.feed()
if self.alert_level != "normal":
print("✅ システム復旧確認")
self.alert_level = "normal"
def start_monitoring(self):
"""多段階監視開始"""
self.warning_watchdog.start_in_thread(daemon=True)
self.critical_watchdog.start_in_thread(daemon=True)
self.emergency_watchdog.start_in_thread(daemon=True)
def stop_monitoring(self):
"""監視停止"""
self.warning_watchdog.stop()
self.critical_watchdog.stop()
self.emergency_watchdog.stop()
# 使用例
multilevel_monitor = MultilevelWatchdog()
multilevel_monitor.start_monitoring()
# 正常時は定期餌やり、異常時は餌やり停止で段階的警告
for i in range(30):
time.sleep(5)
# 時々餌やりを忘れるシミュレーション
if i % 7 != 0: # 7回に1回は餌やりしない
multilevel_monitor.feed_all()
print(f"現在の警告レベル: {multilevel_monitor.alert_level}")
multilevel_monitor.stop_monitoring()
```
## エラーハンドリング・防御機構
### 例外安全性
```python
def safe_callback_example():
"""安全なコールバック実装例"""
def potentially_failing_callback():
"""例外を起こす可能性があるコールバック"""
print("重要な処理を実行中...")
# 何らかの理由で例外が発生
raise RuntimeError("処理中にエラーが発生しました")
# ウォッチドッグは例外を隔離するため、システム全体は継続動作
watchdog = Watchdog(timeout=10, interval=5)
watchdog.setCallback(potentially_failing_callback)
# エラーが発生してもウォッチドッグ自体は停止しない
watchdog.start_in_thread(daemon=True)
# 通常の処理継続
for i in range(5):
time.sleep(3)
watchdog.feed()
print(f"メイン処理継続中: {i}")
watchdog.stop()
print("ウォッチドッグは例外に関係なく正常に停止しました")
safe_callback_example()
```
### 堅牢なスレッド制御
```python
class RobustWatchdog:
"""より堅牢なウォッチドッグ実装"""
def __init__(self, timeout=60, interval=20):
self.base_watchdog = Watchdog(timeout, interval)
self.restart_count = 0
self.max_restarts = 3
def robust_callback(self):
"""自動復旧機能付きコールバック"""
try:
print(f"問題検出(再起動回数: {self.restart_count}/{self.max_restarts}")
if self.restart_count < self.max_restarts:
self.attempt_recovery()
self.restart_count += 1
else:
print("最大再起動回数に達しました。管理者に連絡してください。")
self.emergency_shutdown()
except Exception as e:
print(f"復旧処理中にエラー: {e}")
def attempt_recovery(self):
"""復旧処理の試行"""
print("自動復旧処理を実行中...")
time.sleep(2) # 復旧処理のシミュレーション
# 復旧成功時はカウンターリセット
if self.check_system_health():
self.restart_count = 0
self.base_watchdog.feed()
print("復旧成功")
else:
print("復旧失敗")
def check_system_health(self):
"""システム正常性確認"""
# 実際のヘルスチェックロジック
import random
return random.random() > 0.3 # 70%成功率
def emergency_shutdown(self):
"""緊急停止処理"""
print("緊急停止処理を実行します")
# 安全な停止処理
def start_robust_monitoring(self):
"""堅牢監視開始"""
self.base_watchdog.setCallback(self.robust_callback)
self.base_watchdog.start_in_thread(daemon=True)
def feed(self):
"""餌やり(成功時はカウンターリセット)"""
self.base_watchdog.feed()
self.restart_count = 0
def stop(self):
"""監視停止"""
self.base_watchdog.stop()
# 使用例
robust_monitor = RobustWatchdog(timeout=20, interval=8)
robust_monitor.start_robust_monitoring()
# 不安定なシステムのシミュレーション
for i in range(15):
time.sleep(3)
# 時々システム異常をシミュレート
if random.random() > 0.7: # 30%の確率で異常
print("システム異常発生...")
# 餌やりしない
else:
robust_monitor.feed()
print("システム正常動作")
robust_monitor.stop()
```
## 性能・リソース考慮事項
### 軽量設計の特徴
- 最小限のメモリフットプリント
- 効率的なスレッド利用
- CPU使用量の最適化
### 推奨設定値
```python
# 用途別推奨設定
usage_patterns = {
"realtime_monitoring": {
"timeout": 10, # 10秒
"interval": 2 # 2秒間隔
},
"service_monitoring": {
"timeout": 60, # 1分
"interval": 15 # 15秒間隔
},
"batch_processing": {
"timeout": 300, # 5分
"interval": 60 # 1分間隔
},
"background_tasks": {
"timeout": 1800, # 30分
"interval": 300 # 5分間隔
}
}
```
## 依存関係・要件
### 必須依存関係
- `threading`: スレッド制御
- `time`: 時刻管理
- 標準ライブラリのみ(外部依存なし)
### システム要件
- Python 3.7以上
- マルチスレッド対応OS
- 最小限のシステムリソース
## 注意事項・制限
### 設計上の制限
- 単純なタイムアウトベース監視のみ
- 複雑な条件判定は非対応
- ネットワーク監視等は上位層で実装
### 使用上の注意
- コールバック関数は軽量に保つ
- 長時間ブロックする処理は避ける
- 適切なタイムアウト値の設定が重要
## 関連モジュール
- `threading`: スレッド管理
- `config.py`: 監視設定管理
- `utils.py`: エラーログ・ユーティリティ
- `controller.py`: 監視制御インターフェース
## 将来の改善点
- より複雑な監視条件のサポート
- 監視統計・メトリクス収集機能
- 設定可能な復旧戦略
- 分散監視システムとの連携
- Webインターフェースでの監視状態表示