Capturando erros não tratados no Node.js: O guia completo

Erros não tratados podem causar falhas inesperadas em uma aplicação Node.js. Descubra como capturar e tratar esses erros para melhorar a robustez da sua aplicação.

O que são erros não tratados no Node.js?

Erros não tratados em Node.js são falhas no código que não são capturadas e gerenciadas adequadamente com blocos try/catch ou métodos de tratamento de erro. Isso pode causar o desligamento da aplicação, comportamento inesperado ou perda de dados. No entanto, o Node.js oferece ferramentas poderosas para lidar com esses erros de forma eficaz e garantir que a aplicação continue funcionando sem interrupções.

Neste guia, vamos explorar como capturar e tratar erros não tratados em uma aplicação Node.js, garantindo que sua aplicação seja robusta, confiável e fácil de manter.

1. Capturando erros não tratados no processo principal

No Node.js, qualquer erro não tratado em uma função assíncrona ou Promise pode ser capturado globalmente. Para isso, o process do Node.js oferece os eventos uncaughtException e unhandledRejection, que permitem detectar erros não tratados no seu código e tratar de forma apropriada.

Usando o evento uncaughtException

O evento uncaughtException é acionado sempre que ocorre um erro não tratado em qualquer parte da sua aplicação.

process.on('uncaughtException', (err) => {
    console.error('Erro não tratado:', err);
    process.exit(1);  // Finaliza a aplicação após o erro
});

// Exemplo de erro não tratado
throw new Error('Erro inesperado');

Neste exemplo, sempre que ocorrer um erro não tratado, o evento uncaughtException será acionado, registrando o erro no console e finalizando a aplicação com o código de saída 1, indicando uma falha.

Embora esse evento seja útil, o uso de uncaughtException não é recomendado para capturar erros em tempo de execução em grande escala, pois ele pode mascarar problemas de fundo em sua aplicação. O ideal é sempre tratar os erros diretamente no código com try/catch.

Usando o evento unhandledRejection

O evento unhandledRejection captura rejeições de Promises que não são tratadas com um .catch(). Isso ocorre quando uma Promise é rejeitada e você não faz nada para capturar o erro.

process.on('unhandledRejection', (reason, promise) => {
    console.error('Promise rejeitada sem captura:', promise, 'Razão:', reason);
    process.exit(1);  // Finaliza a aplicação após o erro
});

// Exemplo de rejeição de Promise sem captura
Promise.reject('Erro na promise');

Neste código, o evento unhandledRejection é acionado quando uma Promise é rejeitada sem ser tratada adequadamente. Assim como no uncaughtException, o processo será encerrado após o erro ser capturado.

2. Tratando erros em funções assíncronas

Em funções assíncronas que utilizam async/await, é importante capturar erros com blocos try/catch para evitar que erros não tratados causem falhas na execução.

Exemplo de tratamento com try/catch:

async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data');
        const data = await response.json();
        return data;
    } catch (err) {
        console.error('Erro ao buscar dados:', err);
    }
}

fetchData();

Neste exemplo, usamos try/catch para capturar qualquer erro que ocorra ao realizar uma requisição assíncrona. Isso evita que erros inesperados interrompam a execução da aplicação.

3. Utilizando o domains para capturar erros em múltiplos callbacks

O módulo domain permite que você capture erros de callbacks assíncronos em várias funções, facilitando o tratamento de erros de forma centralizada. No entanto, o domain está depreciado e seu uso não é recomendado para novos projetos. Se você já utiliza domains, é importante considerar alternativas para tratar erros, como Promises e async/await.

Exemplo com domain (deprecado):

const domain = require('domain');

const d = domain.create();

d.on('error', (err) => {
    console.error('Erro capturado pelo domain:', err);
});

d.run(() => {
    // Código que pode lançar erro
    throw new Error('Erro dentro do domínio');
});

Embora funcione para capturar erros dentro do domínio, o domain não é mais recomendado para novos projetos, devido à sua complexidade e à disponibilidade de outras opções mais modernas.

4. Registrando erros em arquivos de log

Uma boa prática é registrar os erros em arquivos de log, para que você possa acompanhar a ocorrência de erros e analisar sua aplicação de maneira eficaz. Usando bibliotecas como Winston ou Morgan, você pode facilmente registrar erros em arquivos e consultar esses logs posteriormente.

Exemplo com Winston para registrar erros:

const winston = require('winston');

const logger = winston.createLogger({
    level: 'error',
    transports: [
        new winston.transports.File({ filename: 'errors.log' })
    ],
});

// Exemplo de erro que será registrado
try {
    throw new Error('Erro capturado');
} catch (err) {
    logger.error('Erro capturado: ' + err.message);
}

Este exemplo usa Winston para gravar erros em um arquivo de log. Isso é útil para auditoria, monitoramento e análise dos problemas da sua aplicação.

5. Conclusão

Capturar e tratar erros não tratados é essencial para garantir que sua aplicação Node.js seja robusta e estável. Usando os eventos uncaughtException e unhandledRejection, você pode monitorar erros globais e tomar ações apropriadas, como registrar os erros ou finalizar o processo. No entanto, sempre que possível, use try/catch em funções assíncronas e trate as Promessas rejeitadas diretamente para evitar falhas.

Lembre-se de registrar todos os erros significativos em arquivos de log ou sistemas de monitoramento para facilitar a análise e a resolução de problemas em produção.

Capturar erros não tratados é uma parte fundamental para a criação de uma aplicação Node.js resiliente. A ferramenta de tratamento de erros permite que a aplicação continue funcionando mesmo quando ocorrem falhas. Usar uncaughtException e unhandledRejection pode ajudar a lidar com erros inesperados de forma global, mas é importante tratar os erros de maneira mais granular com try/catch e Promises.

Além disso, manter um sistema de logs adequado ajuda a monitorar erros e analisar o comportamento da aplicação em produção, facilitando a depuração de problemas e melhorando a experiência do usuário.

Algumas aplicações:

  • Capturar erros de banco de dados não tratados e registrar falhas.
  • Monitorar requisições HTTP para detectar falhas e problemas de conectividade.
  • Usar logs para registrar erros em produção e realizar auditorias de segurança.
  • Simular falhas de rede ou APIs externas e testar como sua aplicação lida com isso.
  • Utilizar testes de integração e unitários para verificar o tratamento de erros em diferentes partes da aplicação.

Dicas para quem está começando

  • Sempre trate erros com try/catch para evitar que erros inesperados interrompam sua aplicação.
  • Use o uncaughtException e unhandledRejection para capturar erros globais e garantir a estabilidade do sistema.
  • Considere usar uma ferramenta de monitoramento de erros em produção para detectar falhas rapidamente.
  • Registre todos os erros críticos em arquivos de log para facilitar a análise posterior.
  • Evite deixar erros não tratados no código de produção. Trate-os o mais cedo possível.

Contribuições de Ricardo Vasconcellos

Compartilhe este tutorial: Como capturar erros não tratados no Node.js?

Compartilhe este tutorial

Continue aprendendo:

Como configurar logs no Node.js?

Configurar logs no Node.js é essencial para monitorar a aplicação em produção, diagnosticar problemas e acompanhar o comportamento do sistema. Descubra como fazer isso de maneira eficiente.

Tutorial anterior

Como lidar com erros de Promise no Node.js?

Erros em Promises podem interromper a execução do código se não forem tratados corretamente. Descubra como capturar e gerenciar erros de Promise no Node.js com as melhores práticas.

Próximo tutorial