Lo sviluppo di applicazioni complesse richiede senza dubbio la presenza di uno strumento fondamentale: il debugger. Per debugging si intende una serie di attività tra cui: controllare lo stato della nostra applicazione in qualsiasi momento, valutare le nostre variabili, controllare che il flusso stia prendendo la strada corretta.
Lo strumento probabilmente più usato per JavaScript è Firebug che ha introdotto ufficiosamente il debugging in un linguaggio multiedrico come JavaScript. Prima eravamo costretti ad utilizzare tonnellate di chiamate a alert
.
Ora che la situazione per JavaScript sembra essere più rosea, abbiamo a disposizione strumenti di debug più o meno complessi suoi principali browser per le applicazioni Web e strumenti desktop per le applicazioni sviluppate con Node.js.
Per debuggare una applicazione Node.js abbiamo, al momento, due tool:
- il debugger a linea di comando presente nativamente in Node.js
- node-inspector
In questo articolo li approfondiremo entrambi, analizzandone vantaggi e svantaggi.
Il debugger interno di Node.js
La versione attuale di Node.js (la 0.8.8) include un tool per il debug avviabile da linea di comando. Anche le versioni precedenti di Node.js lo offrivano anche se le API erano leggermente diverse. Ci occuperemo comunque di analizzare l'ultima versione.
Supponiamo di avere un codice di questo tipo:
var a = 10;
var b = 20;
var c = a + b;
console.log(c);
Avviandolo con l'opzione debug:
node debug prova.js
entreremo nel debugger a linea di comando che si presenta in questo modo:
< debugger listening on port 5858
connecting... ok
break in prova.js:1
1 var a = 10;
2 var b = 20;
3 var c = a + b;
La riga 1 (tipicamente evidenziata in verde) è quella alla quale il debugger si è fermato. I comandi di navigazione che abbiamo ora a disposizione sono:
Comando | Abbreviazione | Descrizione |
---|---|---|
cont | c | per continuare con l'elaborazione |
next | n | per eseguire solamente la riga evidenziata e fermarsi alla successiva |
step | s | per “entrare” nell'esecuzione dello script (approfondiremo successivamente) |
out | o | per “uscire” dall'esecuzione dello script (approfondiremo successivamente) |
Ovviamente al comando cont
lo script terminerà dando come risoltato 30, mentre al comando next
la riga colorata cambierà e diventerà la numero 2.
Un'altra tipologia di comandi sono quelli di analisi:
Comando | Abbreviazione | Descrizione |
---|---|---|
list | n | mostra n righe prima e dopo di quella evidenziata |
watch | exp | aggiunge una nuova espressione |
unwatch | exp | rimuove una espressione |
watchers | mostra tutte le espressioni presenti | |
repl | apre una console di debug nel contesto attuale |
Cerchiamo di capire meglio: supponiamo di essere fermi alla linea 2 (e di aver quindi invocato il comando next
avendo quindi già instanziato e valorizzato la variabile a
). Invocando il comando list(1)
otterremo:
1 var a = 10;
2 var b = 20;
3 var c = a + b;
Proviamo ora a creare un watcher:
debug> watch('a')
In questo modo abbiamo detto al debugger che siamo intenzionati a voler seguire il valore della variabile a durante l'esecuzione dello script. Attenzione che la console di debug è una vera e propria shell JavaScript differenziata dallo script originale quindi i nomi delle variabili devono essere considerati come stringhe.
debug> watchers
0: a = 10
Chiedendo l'elenco dei watchers il debugger ci ha ritornato che la variabile a in questo momento vale 10: tutto corretto. Proviamo ad aggiungere un nuovo watcher:
debug> watch('a')
debug> watchers
0: a = 10
1: b = undefined
Correttamente la variabile b non è ancora stata definita. Proviamo a proseguire grazie a next
:
debug> next
break in prova.js:3
Watchers:
0: a = 10
1: b = 20
1 var a = 10;
2 var b = 20;
3 var c = a + b;
4 console.log(c);
Abbiamo scoperto due cose: la prima è che in fase di navigazione i watchers vengono riportati automaticamente e la seconda che la variabile b ora è stata instanziata e vale correttamente 20.
I watchers servono per tenere traccia di una variabile durante l'esecuzione, ma se vorremmo conoscere il suo valore solamente una tantum possiamo utilizzare repl
:
debug> repl
Press Ctrl + C to leave debug repl
> a
10
> b
20
Tutto secondo le aspettative!
Analizziamo ora le funzioni step e out viste in precedenza. Per poterlo fare ci serve modificare leggermente il codice in:
var somma = function(val1, val2) {
var somma = val1 + val2;
return somma;
}
var a = 10;
var b = 20;
var c = somma(a, b);
console.log(c);
Avviamo ora l'applicazione in debug e fermiamoci alla linea 7:
break in prova.js:7
5 var a = 10;
6 var b = 20;
7 var c = somma(a, b);
8 console.log(c);
Rieseguendo ora il comando di next verremmo indirizzati verso la linea 8, saltando di fatto il codice eseguito all'interno della funzione somma. I comandi step e out servono proprio a questo. Invocando step alla riga 7 il debugger ci porterà direttamente all'interno della funzione somma, quindi alla linea 2:
break in prova.js:2
1 var somma = function(val1, val2) {
2 var somma = val1 + val2;
3 return somma;
4 }
Questo ci permette di analizzare anche il codice in tutte le sue sotto-funzioni. Una volta dentro possiamo proseguire normalmente con next che ci farà uscire una volta terminata la funzione, oppure con out
per uscire immediatamente.
Il debugger presenta ulteriori funzioni più avanzate come per esempio la possibilità di gestire i break point o di debuggare script già avviati tramite l'invio di un signal. Per approfondire queste tematiche vi rimando alla documentazione ufficiale.
node-inspector
node-inspector è un plugin che permette di accedere al debugger di Node.js in una maniera più intuitiva e comoda, sfruttando il debugger già presente all'interno di un qualsiasi browser basato su WebKit. Essendo un plugin che aggiunge un nuovo comando alla macchina dobbiamo installarlo in modalità globale, quindi con permessi di amministratore e con il parametro -g
:
sudo npm install -g node-inspector
Una volta installato per visualizzare il debugger sono necessari due step banali:
- avviare lo script con
node --debug prova.js
(o con--debug-brk
per inserire un break alla prima riga, come avveniva con il debugger di Node.js) - invocare il comando
node-inspector
per avviare il servizio web per la gestione del debugger.
Avviamoli entrambi in due console differenti:
> node --debug-brk prova.js
debugger listening on port 5858
> node-inspector
info - socket.io started
visit http://0.0.0.0:8080/debug?port=5858 to start debugging
e avviamo un browser WebKit (nel mio caso Chromium) facendolo puntare a localhost sulla porta 8080. Ecco quello che visualizzeremo:
Praticamente abbiamo un ambiente grafico di debugger per il nostro script, sfruttando quello di WebKit. Meglio di così?
Ovviamente da questa interfaccia avremo accesso a tutte le funzionalità viste in precedenza esattamente come sappiamo già fare per gli applicativi web.
Per approfondire ulteriormente ecco il link alla pagina GitHub di node-inspector.
Confronto finale
La differenza tra i due debugger sta nella GUI, che rende node-inspector più semplice da usare. Tuttavia non sempre è possibile usare node-inspector soprattutto in ambienti di produzione dove non è possibile installare nuovi applicativi, avere accesso ad interfacce testuali o aprire ulteriori porte TCP verso l'esterno. L'ideale è quindi sfruttare node-inspector durante il processo di sviluppo, ma non escludere completamente il debugger interno perchè potrà sempre tornare utile.