Debugando sua app Node com Chrome DevTools
Não é raro termos que debugar nossa aplicação à procura de problemas, muitas vezes por aqueles que ocorrem insidiosamente. Como em outras plataformas, Node.js possui um debugger padrão que pode estar integrado a editores e IDE’s do mercado. Contudo, podemos acessá-lo diretamente pelo Chrome DevTools utilizando-o como ferramenta de depuração agnóstica de editor ou IDE. Para que possamos ver essa integração em ação, primeiro veremos o uso do debugger através do terminal. Você pode pular esta parte se assim preferir e ir direto para a depuração do programa no Chrome DevTools.
Introdução rápida à depuração em Node.js através do terminal
Todo o procedimento aqui pode ser feito em sua máquina, contanto que esteja utilizando Node 8.0 ou superior
Vamos criar o script index.js
que utiliza recursos do Node.js versão 8.0 para criar um arquivo e exbir seu tamanho em seguida através do terminal:
// index.js
const { promisify } = require('util');
const writeFile = promisify(require('fs').writeFile);
const stat = promisify(require('fs').stat);
(async function() {
try {
const filePath = 'arquivo.txt';
await writeFile(filePath, 'conteúdo arquivo');
console.log('arquivo criado com sucesso!');
const fileStat = await stat(filePath);
console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
} catch(err) {
console.log(err);
}
})();
Para debugarmos nosso arquivo, basta chamá-lo através do interpretador do Node com auxílio do parâmetro inspect
:
node inspect index
Por padrão, o debugger adicionará um breakpoint
na primeira linha de execução do programa, aguardando nossa intervenção:
MacBook-Pro-de-Flavio:Desktop flavioalmeida$ node inspect index.js
< Debugger listening on ws://127.0.0.1:9229/9de73f77-46ec-4f7d-9d98-967d4f7544b5
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in index.js:1
> 1 (function (exports, require, module, __filename, __dirname) { const { promisify } = require('util');
2 const writeFile = promisify(require('fs').writeFile);
3 const stat = promisify(require('fs').stat);
debug>
Para que o programa prossiga até o próximo breakpoint
, usamos a instrução c (ou cont):
debug> c
< arquivo criado com sucesso!
< Tamanho do arquivo 17 bytes
< Waiting for the debugger to disconnect...
debug>
Como não havia nenhum outro breakpoint
no programa, ele foi executado até o fim. Vamos parar o debugger utilizando Ctrl + C / Cmd + C
duas vezes.
Adicionaremos desta vez um breakpoint
em nosso programa:
// index.js
const { promisify } = require('util');
const writeFile = promisify(require('fs').writeFile);
const stat = promisify(require('fs').stat);
(async function() {
try {
const filePath = 'arquivo.txt';
await writeFile(filePath, 'conteúdo arquivo');
console.log('arquivo criado com sucesso!');
const fileStat = await stat(filePath);
// breakpoint aqui!
debugger;
console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
} catch(err) {
console.log(err);
}
})();
E mais uma vez vamos rodá-lo com o comando:
node inspect index
Teremos o mesmo comportamento que antes, pois o breakpoint
default na primeira linha do script será disparado:
MacBook-Pro-de-Flavio:Desktop flavioalmeida$ node inspect index.js
< Debugger listening on ws://127.0.0.1:9229/9de73f77-46ec-4f7d-9d98-967d4f7544b5
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in index.js:1
> 1 (function (exports, require, module, __filename, __dirname) { const { promisify } = require('util');
2 const writeFile = promisify(require('fs').writeFile);
3 const stat = promisify(require('fs').stat);
debug>
Agora, ao executarmos c
o fluxo do nosso programa continuará até o proximo breakpoint
:
debug> c
< arquivo criado com sucesso!
break in index.js:11
9 await writeFile(filePath, 'conteúdo arquivo');
10 console.log('arquivo criado com sucesso!');
>11 debugger; // breakpoint aqui!
12 const fileStat = await stat(filePath);
13
debug>
Queremos saber o valor de fileStat
, mas para isso precisamos fazer com que a instrução seja processada. Podemos dar pequenos passos no debugger através do comando n
(ou next
):
debug> n
break in index.js:12
10 console.log('arquivo criado com sucesso!');
11 debugger; // breakpoint aqui!
>12 const fileStat = await stat(filePath);
13
14 console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
debug>
Porém, há um detalhe. Nosso debugger parou para avaliar a instrução stat(filePath)
. Ao executarmos mais uma vez a instrução n
, a instrução await
será processada. Por fim, na terceira vez que entrarmos com n
, teremos o resultado na variável fileStart
, pois já estaremos posicionados para executar a instrução console.log()
:
debug> n
break in index.js:12
10 console.log('arquivo criado com sucesso!');
11 debugger; // breakpoint aqui!
>12 const fileStat = await stat(filePath); // executando stat
13
14 console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
debug> n
break in index.js:12
10 console.log('arquivo criado com sucesso!');
11 debugger; // breakpoint aqui!
>12 const fileStat = await stat(filePath); // processando await
13
14 console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
debug> n
break in index.js:14
12 const fileStat = await stat(filePath);
13
>14 console.log(`Tamanho do arquivo ${fileStat.size} bytes`);
15 } catch(err) {
16 console.log(err);
Agora, precisamos abrir o repl
do Node que ganhará como contexto o ponto no qual nosso código esta parado:
debug> repl
Press Ctrl + C to leave debug repl
>
Por fim, vamos verificar o valor de fileStat
:
debug> repl
Press Ctrl + C to leave debug repl
> fileStat
{ dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
... }
>
Através de Ctrl + C / Cmd + C
, saimos do repl e voltamos para o debugger. Ao utilizarmos o comando c
mais uma vez, a aplicação chegará até o fim.
Há outros comando como o
(out) e p
(pause) que permitem pular uma instrução e pausar a depuração.
Excelente, agora que já temos um overview de como é feita a depuração de uma aplicação Node.js pelo terminal, vamos realizar os mesmos passos através do Chrome DevTools. Mas antes de cairmos dentro do assunto, remova o breakpoint debugger;
de index.js
. Vamos adicioná-lo de outra maneira.
Preparando a depuração para o Chrome DevTools
Para debugarmos nossa aplicação através do Chrome, também usaremos o parâmetro inspect
, porém utilizaremos node --inspect index
em vez de node inspect index
apenas.
MBP-de-Flavio:Desktop flavioalmeida$ node --inspect index
Debugger listening on ws://127.0.0.1:9229/128e9932-2323-4309-b00c-0e9909584334
For help see https://nodejs.org/en/docs/inspector
arquivo criado com sucesso!
Tamanho do arquivo 17 bytes
MBP-de-Flavio:Desktop flavioalmeida$
Ops! Temos um problema! Não há um breakpoint padrão na aplicação que a pause temporariamente para termos chance de depurá-la. Inclusive, nem adiantará adicionarmos debugger;
no início do arquivo que ele não será respeitado.
Para solucionarmos o problema anterior, basta mudarmos ligeiramente a instrução utilizada para node --inspect-brk index
:
MBP-de-Flavio:Desktop flavioalmeida$ node --inspect-brk index
Debugger listening on ws://127.0.0.1:9229/0bc2d5f3-fde1-4455-bdbe-9dfa3552a82e
For help see https://nodejs.org/en/docs/inspector
O comando anterior realizou um breakpoint
na primeira linha do arquivo. Como o debugger ainda continua rodando, podemos acessá-lo através do Chrome DevTool.
Debugando sua app Node com Chrome DevTools
Para debugarmos nossa aplicação através do Chrome, precisamos acessar o endereço chrome://inspect. Será exibida uma tela com uma série de informações.
A informação que nos interessa por agora é a Target. Ela possui um link que ao ser clicado nos direcionará para o depurador do Chrome DevTools.
Nele, visualizaremos nosso código envolvido por uma closure do depurador do Node.js. Vamos clicar na linha 12, exatamente no número da linha para adicionarmos um breakpoint
:
Agora, na barra lateral superior direita, na barra de botões , através do botão play
executamos o comando c
(continue
) fazendo com que nosso código execute até encontrar o próximo breakpoint
que definimos. Neste ponto de parada, podemos escrutinar o valor da variável fileStat
passando o mouse por cima. Também podemos marcar a variável e com o botão direito executar uma série de operações:
Um ambiente mais amistoso do que aquele oferecido pela depuração através do terminal.
Conclusão
Podemos debugar uma aplicação Node.js através do terminal ou através do Chrome DevTools. Este último fornece um ambiente de depuração visual independente do editor de texto ou IDE escolhida.