11 KiB
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:
pip install psutil
Integrazione nel tuo progetto
Hai due opzioni:
Opzione 1: Copia diretta del modulo
# Copia il file nella tua applicazione
cp target_simulator/utils/resource_monitor.py <tuo_progetto>/
Opzione 2: Import dal progetto Target Simulator
from target_simulator.utils.resource_monitor import ResourceMonitor
🚀 Quick Start
Esempio 1: Console Semplice
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
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)
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
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"
- Esempio output:
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 MiBmemory_percent(float): Percentuale memoria del sistemamemory_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 è attivopoll_interval(float): Intervallo di polling corrente
Classe TkinterResourceMonitor
Specializzazione di ResourceMonitor per GUI Tkinter con aggiornamento thread-safe automatico.
Constructor
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.StringVarda aggiornare automaticamente- Altri parametri come
ResourceMonitor
Esempio:
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.
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:
$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:
python target_simulator/utils/examples/resource_monitor_example.py
Il file contiene 5 esempi:
- Console semplice - Stampa su terminale
- Lettura sincrona - Lettura singola senza thread
- Callback personalizzato - Analisi e logging custom
- Tkinter GUI - Integrazione completa con finestra
- Monitoring altro processo - Specifica PID esplicito
🔧 Integrazione nel Tuo Progetto
Scenario 1: Applicazione Console
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)
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)
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%).
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"
# 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:
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:
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:
- Controlla gli esempi in
examples/resource_monitor_example.py - Verifica che psutil sia installato correttamente
- Prova il test standalone del modulo
Versione: 1.0
Estratto da: Target Simulator Project
Data: Novembre 2025