431 lines
11 KiB
Markdown
431 lines
11 KiB
Markdown
# ResourceMonitor - Modulo di Monitoring Risorse
|
|
|
|
Modulo Python standalone e riutilizzabile per il monitoring in tempo reale di CPU, RAM e Thread di un processo. Estratto dal progetto Target Simulator e reso completamente indipendente per essere integrato facilmente in altre applicazioni.
|
|
|
|
## 🎯 Caratteristiche
|
|
|
|
- ✅ **Monitoring in background** tramite thread daemon
|
|
- ✅ **Aggiornamenti periodici** configurabili (polling interval personalizzabile)
|
|
- ✅ **Callback personalizzati** per gestire i dati come preferisci
|
|
- ✅ **Integrazione Tkinter** con classe specializzata `TkinterResourceMonitor`
|
|
- ✅ **Thread-safe** per uso in applicazioni multi-thread
|
|
- ✅ **Gestione errori robusta** - non crasha mai l'applicazione
|
|
- ✅ **Metriche accurate**:
|
|
- CPU normalizzata per sistemi multi-core (0-100%)
|
|
- Memoria USS (Unique Set Size) quando disponibile, fallback a RSS
|
|
- Conteggio thread attivi
|
|
- ✅ **Zero dipendenze obbligatorie** - graceful degradation se psutil non disponibile
|
|
|
|
## 📦 Installazione
|
|
|
|
### Dipendenze
|
|
|
|
Il modulo funziona senza dipendenze obbligatorie, ma **richiede psutil** per il monitoring effettivo:
|
|
|
|
```powershell
|
|
pip install psutil
|
|
```
|
|
|
|
### Integrazione nel tuo progetto
|
|
|
|
Hai due opzioni:
|
|
|
|
**Opzione 1: Copia diretta del modulo**
|
|
```powershell
|
|
# Copia il file nella tua applicazione
|
|
cp target_simulator/utils/resource_monitor.py <tuo_progetto>/
|
|
```
|
|
|
|
**Opzione 2: Import dal progetto Target Simulator**
|
|
```python
|
|
from target_simulator.utils.resource_monitor import ResourceMonitor
|
|
```
|
|
|
|
## 🚀 Quick Start
|
|
|
|
### Esempio 1: Console Semplice
|
|
|
|
```python
|
|
from target_simulator.utils.resource_monitor import ResourceMonitor
|
|
|
|
def print_stats(stats_string):
|
|
print(f"\r{stats_string}", end="", flush=True)
|
|
|
|
monitor = ResourceMonitor(
|
|
update_callback=print_stats,
|
|
poll_interval=1.0 # Aggiorna ogni secondo
|
|
)
|
|
|
|
monitor.start()
|
|
|
|
# ... la tua applicazione gira ...
|
|
|
|
monitor.stop() # Quando hai finito
|
|
```
|
|
|
|
### Esempio 2: Tkinter GUI
|
|
|
|
```python
|
|
import tkinter as tk
|
|
from target_simulator.utils.resource_monitor import TkinterResourceMonitor
|
|
|
|
root = tk.Tk()
|
|
|
|
# StringVar per visualizzare le statistiche
|
|
status_var = tk.StringVar()
|
|
tk.Label(root, textvariable=status_var, font=("Courier", 10)).pack()
|
|
|
|
# Crea e avvia il monitor
|
|
monitor = TkinterResourceMonitor(
|
|
tk_widget=root,
|
|
string_var=status_var,
|
|
poll_interval=1.0
|
|
)
|
|
monitor.start()
|
|
|
|
# Alla chiusura
|
|
def on_closing():
|
|
monitor.stop()
|
|
root.destroy()
|
|
|
|
root.protocol("WM_DELETE_WINDOW", on_closing)
|
|
root.mainloop()
|
|
```
|
|
|
|
### Esempio 3: Lettura Sincrona (Senza Thread)
|
|
|
|
```python
|
|
from target_simulator.utils.resource_monitor import ResourceMonitor
|
|
|
|
# Callback dummy se non serve monitoring continuo
|
|
monitor = ResourceMonitor(update_callback=lambda s: None)
|
|
|
|
# Ottieni statistiche on-demand
|
|
stats = monitor.get_current_stats()
|
|
|
|
if stats:
|
|
print(f"CPU: {stats['cpu_percent']:.1f}%")
|
|
print(f"RAM: {stats['memory_mb']:.1f} MB ({stats['memory_percent']:.1f}%)")
|
|
print(f"Thread: {stats['thread_count']}")
|
|
```
|
|
|
|
## 📚 Documentazione API
|
|
|
|
### Classe `ResourceMonitor`
|
|
|
|
Monitor delle risorse di sistema che gira in background.
|
|
|
|
#### Constructor
|
|
|
|
```python
|
|
ResourceMonitor(
|
|
update_callback: Callable[[str], None],
|
|
poll_interval: float = 1.0,
|
|
process_pid: Optional[int] = None
|
|
)
|
|
```
|
|
|
|
**Parametri:**
|
|
- `update_callback`: Funzione chiamata ad ogni aggiornamento con la stringa formattata delle statistiche
|
|
- Esempio output: `"CPU 5% · MEM 120MB (2.5%) [USS] · Thr 12"`
|
|
- `poll_interval`: Intervallo in secondi tra le letture (default: 1.0)
|
|
- `process_pid`: PID del processo da monitorare (default: processo corrente)
|
|
|
|
#### Metodi Principali
|
|
|
|
##### `start() -> bool`
|
|
Avvia il monitoring in background.
|
|
|
|
**Returns:** `True` se avviato con successo, `False` se psutil non disponibile o già in esecuzione
|
|
|
|
##### `stop() -> None`
|
|
Ferma il monitoring. Thread-safe, può essere chiamato più volte.
|
|
|
|
##### `get_current_stats() -> Optional[Dict[str, Any]]`
|
|
Ottiene statistiche correnti in modo sincrono (senza avviare il thread).
|
|
|
|
**Returns:** Dizionario con chiavi:
|
|
- `cpu_percent` (float): Percentuale CPU normalizzata (0-100)
|
|
- `memory_mb` (float): Memoria in MiB
|
|
- `memory_percent` (float): Percentuale memoria del sistema
|
|
- `memory_type` (str): "USS" o "RSS"
|
|
- `thread_count` (int): Numero di thread
|
|
|
|
Oppure `None` se psutil non disponibile o errore.
|
|
|
|
#### Proprietà
|
|
|
|
- `is_running` (bool): True se il monitor è attivo
|
|
- `poll_interval` (float): Intervallo di polling corrente
|
|
|
|
### Classe `TkinterResourceMonitor`
|
|
|
|
Specializzazione di `ResourceMonitor` per GUI Tkinter con aggiornamento thread-safe automatico.
|
|
|
|
#### Constructor
|
|
|
|
```python
|
|
TkinterResourceMonitor(
|
|
tk_widget, # Any widget with .after() method
|
|
string_var, # tk.StringVar to update
|
|
poll_interval: float = 1.0,
|
|
process_pid: Optional[int] = None
|
|
)
|
|
```
|
|
|
|
**Parametri:**
|
|
- `tk_widget`: Qualsiasi widget Tkinter (root, Frame, etc.) con metodo `.after()`
|
|
- `string_var`: `tk.StringVar` da aggiornare automaticamente
|
|
- Altri parametri come `ResourceMonitor`
|
|
|
|
**Esempio:**
|
|
```python
|
|
import tkinter as tk
|
|
from target_simulator.utils.resource_monitor import TkinterResourceMonitor
|
|
|
|
root = tk.Tk()
|
|
stats_var = tk.StringVar()
|
|
|
|
monitor = TkinterResourceMonitor(root, stats_var)
|
|
monitor.start()
|
|
```
|
|
|
|
### Funzioni Utility
|
|
|
|
#### `is_psutil_available() -> bool`
|
|
Verifica se psutil è disponibile nel sistema.
|
|
|
|
```python
|
|
from target_simulator.utils.resource_monitor import is_psutil_available
|
|
|
|
if is_psutil_available():
|
|
print("psutil OK")
|
|
else:
|
|
print("Installa psutil: pip install psutil")
|
|
```
|
|
|
|
## 🧪 Testing & Esempi
|
|
|
|
### Esecuzione Test Standalone
|
|
|
|
Il modulo può essere eseguito direttamente per un test rapido:
|
|
|
|
```powershell
|
|
$env:PYTHONPATH='C:\src\____GitProjects\target_simulator'
|
|
python -m target_simulator.utils.resource_monitor
|
|
```
|
|
|
|
Output esempio:
|
|
```
|
|
=== Test ResourceMonitor ===
|
|
Monitoraggio risorse per 10 secondi...
|
|
Premi Ctrl+C per interrompere
|
|
|
|
CPU 8% · MEM 145MB (1.8%) [USS] · Thr 15
|
|
|
|
Monitor fermato.
|
|
|
|
Statistiche finali:
|
|
CPU: 8.2%
|
|
Memoria: 145.3 MB (1.8%)
|
|
Tipo memoria: USS
|
|
Thread: 15
|
|
```
|
|
|
|
### Esempi Completi
|
|
|
|
Esegui il file di esempi interattivo:
|
|
|
|
```powershell
|
|
python target_simulator/utils/examples/resource_monitor_example.py
|
|
```
|
|
|
|
Il file contiene 5 esempi:
|
|
1. **Console semplice** - Stampa su terminale
|
|
2. **Lettura sincrona** - Lettura singola senza thread
|
|
3. **Callback personalizzato** - Analisi e logging custom
|
|
4. **Tkinter GUI** - Integrazione completa con finestra
|
|
5. **Monitoring altro processo** - Specifica PID esplicito
|
|
|
|
## 🔧 Integrazione nel Tuo Progetto
|
|
|
|
### Scenario 1: Applicazione Console
|
|
|
|
```python
|
|
from target_simulator.utils.resource_monitor import ResourceMonitor
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def log_resources(stats: str):
|
|
logger.info(f"Resources: {stats}")
|
|
|
|
# Avvia monitoring all'inizio dell'app
|
|
monitor = ResourceMonitor(log_resources, poll_interval=5.0)
|
|
monitor.start()
|
|
|
|
# ... la tua applicazione ...
|
|
|
|
# Cleanup alla fine
|
|
monitor.stop()
|
|
```
|
|
|
|
### Scenario 2: Web Server (Flask/FastAPI)
|
|
|
|
```python
|
|
from target_simulator.utils.resource_monitor import ResourceMonitor
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
current_stats = {"data": "Not started"}
|
|
|
|
def update_stats(stats: str):
|
|
current_stats["data"] = stats
|
|
|
|
monitor = ResourceMonitor(update_stats)
|
|
monitor.start()
|
|
|
|
@app.route('/health')
|
|
def health():
|
|
return {"status": "ok", "resources": current_stats["data"]}
|
|
|
|
if __name__ == '__main__':
|
|
try:
|
|
app.run()
|
|
finally:
|
|
monitor.stop()
|
|
```
|
|
|
|
### Scenario 3: GUI Tkinter (Application Completa)
|
|
|
|
```python
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
from target_simulator.utils.resource_monitor import TkinterResourceMonitor
|
|
|
|
class MyApplication(tk.Tk):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
# UI
|
|
self.title("My App")
|
|
self.stats_var = tk.StringVar(value="Inizializzazione...")
|
|
|
|
# Status bar con resource monitor
|
|
status_bar = ttk.Frame(self, relief=tk.SUNKEN)
|
|
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
|
|
|
ttk.Label(status_bar, textvariable=self.stats_var).pack(
|
|
side=tk.RIGHT, padx=5
|
|
)
|
|
|
|
# Avvia monitor
|
|
self.monitor = TkinterResourceMonitor(
|
|
tk_widget=self,
|
|
string_var=self.stats_var,
|
|
poll_interval=1.0
|
|
)
|
|
self.monitor.start()
|
|
|
|
self.protocol("WM_DELETE_WINDOW", self.on_closing)
|
|
|
|
def on_closing(self):
|
|
self.monitor.stop()
|
|
self.destroy()
|
|
|
|
if __name__ == '__main__':
|
|
app = MyApplication()
|
|
app.mainloop()
|
|
```
|
|
|
|
## ⚙️ Dettagli Tecnici
|
|
|
|
### Normalizzazione CPU Multi-Core
|
|
|
|
Su sistemi multi-core, `psutil.Process.cpu_percent()` può ritornare valori >100% perché misura l'utilizzo totale su tutti i core. Il `ResourceMonitor` normalizza automaticamente questo valore dividendo per il numero di CPU logiche, rendendo il risultato comparabile con Task Manager (scala 0-100%).
|
|
|
|
```python
|
|
cpu_proc = proc.cpu_percent(None) # Può essere >100%
|
|
ncpu = psutil.cpu_count(logical=True) or 1
|
|
cpu = cpu_proc / ncpu # Normalizzato 0-100%
|
|
```
|
|
|
|
### Memoria: USS vs RSS
|
|
|
|
Il monitor preferisce **USS (Unique Set Size)** quando disponibile:
|
|
- **USS**: Memoria unica del processo (più accurata, esclude shared memory)
|
|
- **RSS**: Resident Set Size (fallback, include shared memory)
|
|
|
|
USS è più vicino al "Private Working Set" mostrato da Task Manager.
|
|
|
|
### Thread Safety
|
|
|
|
Il monitoring avviene in un thread daemon separato. Gli aggiornamenti UI (Tkinter) sono schedulati sul main thread usando `widget.after(0, ...)` per garantire thread-safety.
|
|
|
|
### Gestione Errori
|
|
|
|
Il modulo è progettato per **mai** crashare l'applicazione host:
|
|
- Se psutil non disponibile → graceful degradation
|
|
- Errori nel callback → ignorati, monitoring continua
|
|
- Widget Tkinter distrutto → errori soppressi
|
|
- Processo terminato → thread si ferma automaticamente
|
|
|
|
## 🐛 Troubleshooting
|
|
|
|
### "psutil non disponibile"
|
|
|
|
```powershell
|
|
# Installa psutil
|
|
pip install psutil
|
|
|
|
# Verifica installazione
|
|
python -c "import psutil; print(psutil.__version__)"
|
|
```
|
|
|
|
### Valori CPU strani (molto alti o molto bassi)
|
|
|
|
La prima lettura CPU può essere imprecisa. Il monitor fa un "priming" iniziale (`proc.cpu_percent(None)`) per risolvere questo problema.
|
|
|
|
### Memory leak con Tkinter
|
|
|
|
Assicurati di chiamare sempre `monitor.stop()` prima di chiudere l'applicazione:
|
|
|
|
```python
|
|
def on_closing():
|
|
monitor.stop() # IMPORTANTE!
|
|
root.destroy()
|
|
|
|
root.protocol("WM_DELETE_WINDOW", on_closing)
|
|
```
|
|
|
|
### Thread non si ferma
|
|
|
|
Il thread è daemon, quindi termina automaticamente quando l'app termina. `stop()` ferma il polling ma se serve attendere la terminazione:
|
|
|
|
```python
|
|
monitor.stop()
|
|
if monitor._thread:
|
|
monitor._thread.join(timeout=2.0)
|
|
```
|
|
|
|
## 📄 Licenza
|
|
|
|
Stesso del progetto parent (Target Simulator).
|
|
|
|
## 🤝 Contributi
|
|
|
|
Questo modulo è estratto da un progetto più grande. Per bug o miglioramenti, aggiorna il file originale in `target_simulator/utils/resource_monitor.py`.
|
|
|
|
## 📞 Supporto
|
|
|
|
Per domande o problemi:
|
|
1. Controlla gli esempi in `examples/resource_monitor_example.py`
|
|
2. Verifica che psutil sia installato correttamente
|
|
3. Prova il test standalone del modulo
|
|
|
|
---
|
|
|
|
**Versione:** 1.0
|
|
**Estratto da:** Target Simulator Project
|
|
**Data:** Novembre 2025
|