python-resource-monitor/RESOURCE_MONITOR_README.md
2025-11-26 08:29:04 +01:00

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