Comecei a experimentar o node.js há alguns dias. Percebi que o Nó é encerrado sempre que tenho uma exceção não tratada no meu programa. Isso é diferente do contêiner de servidor normal ao qual fui exposto, onde apenas o Thread de Trabalho morre quando ocorrem exceções sem tratamento e o contêiner ainda poderá receber a solicitação. Isso levanta algumas questões:
- É
process.on('uncaughtException')
a única maneira eficaz de se proteger contra isso? - Vai
process.on('uncaughtException')
pegar a exceção não tratada durante a execução de processos assíncronos bem? - Existe um módulo que já foi criado (como enviar e-mail ou gravar em um arquivo) que eu poderia aproveitar no caso de exceções não capturadas?
Eu apreciaria qualquer ponteiro / artigo que me mostrasse as práticas recomendadas comuns para lidar com exceções não capturadas no node.js
try .. catch
, e verificar isso também é feito para todos os seus libssetTimeout
ousetInterval
algo desse tipo enterrado em algum lugar profundo que não possa ser capturado pelo seu código.Respostas:
Atualização: Joyent agora tem seu próprio guia . As informações a seguir são mais um resumo:
Erros "lançando" com segurança
Idealmente, gostaríamos de evitar erros não capturados o máximo possível; portanto, em vez de literalmente lançar o erro, podemos "com segurança" lançar o erro usando um dos métodos a seguir, dependendo da nossa arquitetura de código:
Para código síncrono, se ocorrer um erro, retorne o erro:
Para código baseado em retorno de chamada (ou seja, assíncrono), o primeiro argumento do retorno de chamada é
err
, se ocorrer um erroerr
é o erro, se um erro não ocorrer,err
seránull
. Quaisquer outros argumentos seguem oerr
argumento:Para código com eventos , em que o erro pode ocorrer em qualquer lugar, em vez de gerar o erro, inicie o
error
evento :Erros "pegando" com segurança
Às vezes, porém, ainda pode haver código que gera um erro em algum lugar, o que pode levar a uma exceção não capturada e a uma possível falha de nosso aplicativo se não o capturarmos com segurança. Dependendo da nossa arquitetura de código, podemos usar um dos seguintes métodos para capturá-lo:
Quando sabemos onde o erro está ocorrendo, podemos agrupar essa seção em um domínio node.js.
Se soubermos onde o erro está ocorrendo, é um código síncrono e, por qualquer motivo, não puder usar domínios (talvez a versão antiga do nó), podemos usar a instrução try catch:
No entanto, tenha cuidado para não usar
try...catch
no código assíncrono, pois um erro gerado de forma assíncrona não será detectado:Se você deseja trabalhar
try..catch
em conjunto com o código assíncrono, ao executar o Nó 7.4 ou superior, você pode usarasync/await
nativamente para gravar suas funções assíncronas.Outra coisa com a qual ter cuidado
try...catch
é o risco de agrupar seu retorno de chamada de conclusão dentro datry
instrução da seguinte maneira:Essa tarefa é muito fácil, pois seu código se torna mais complexo. Como tal, é melhor usar domínios ou retornar erros para evitar (1) exceções não capturadas no código assíncrono (2) a tentativa de captura de execução que você não deseja. Em idiomas que permitem o encadeamento adequado, em vez do estilo de máquina de eventos assíncrona do JavaScript, isso é menos problemático.
Finalmente, no caso de um erro não detectado ocorrer em um local que não foi envolvido em um domínio ou em uma instrução try catch, podemos fazer com que o nosso aplicativo não falhe usando o
uncaughtException
ouvinte (no entanto, isso pode colocar o aplicativo em um estado desconhecido ):fonte
try catch
? Como eu adoraria apoiar isso com evidências. Também foi corrigido o exemplo de sincronização.A seguir, é apresentado um resumo e uma curadoria de várias fontes diferentes sobre este tópico, incluindo exemplos de código e citações de postagens de blog selecionadas. A lista completa de práticas recomendadas pode ser encontrada aqui
Práticas recomendadas para o tratamento de erros do Node.JS
Número1: use promessas para tratamento de erros assíncronos
TL; DR: o tratamento de erros assíncronos no estilo de retorno de chamada é provavelmente o caminho mais rápido para o inferno (também conhecido como pirâmide do destino). O melhor presente que você pode dar ao seu código é usar uma biblioteca de promessas respeitável, que fornece uma sintaxe de código muito compacta e familiar, como try-catch
Caso contrário: o estilo de retorno de chamada Node.JS, função (err, resposta), é uma maneira promissora de código não-sustentável, devido à mistura de tratamento de erros com código casual, aninhamento excessivo e padrões de codificação desajeitados
Exemplo de código - bom
exemplo de código anti-padrão - tratamento de erro no estilo de retorno de chamada
Citação do blog: "Temos um problema com promessas" (no blog pouchdb, classificado em 11 pelas palavras-chave "Promessas do nó")
Número2: use apenas o objeto de erro interno
TL; DR: É bastante comum ver código que gera erros como string ou como um tipo personalizado - isso complica a lógica de manipulação de erros e a interoperabilidade entre os módulos. Se você rejeita uma promessa, lança uma exceção ou emite um erro - o uso do objeto Erro interno do Node.JS aumenta a uniformidade e evita a perda de informações de erro.
Caso contrário: Ao executar algum módulo, a incerteza de que tipo de erro ocorre em retorno - dificulta muito o raciocínio e a manipulação da exceção que se aproxima. Mesmo vale a pena, usar tipos personalizados para descrever erros pode levar à perda de informações críticas sobre erros, como o rastreamento de pilha!
Exemplo de código - fazendo certo
exemplo de código anti-padrão
Citação do blog: "Uma string não é um erro" (no blog devthought, classificou 6 para as palavras-chave “Node.JS error object”)
Número 3: Distinguir erros operacionais x programadores
TL; DR: erros de operações (por exemplo, a API recebeu uma entrada inválida) referem-se a casos conhecidos em que o impacto do erro é totalmente compreendido e pode ser tratado com cuidado. Por outro lado, o erro do programador (por exemplo, tentar ler variáveis indefinidas) refere-se a falhas de código desconhecidas que determinam reiniciar o aplicativo normalmente.
Caso contrário: você sempre poderá reiniciar o aplicativo quando um erro aparecer, mas por que decepcionar ~ 5000 usuários on-line devido a um erro pequeno e previsto (erro operacional)? o contrário também não é o ideal - manter o aplicativo ativo quando ocorreu um problema desconhecido (erro do programador) pode levar a um comportamento imprevisível. A diferenciação dos dois permite agir com tato e aplicar uma abordagem equilibrada com base no contexto fornecido
Exemplo de código - fazendo certo
exemplo de código - marcando um erro como operacional (confiável)
Citação do blog : "Caso contrário, você corre o risco do estado" (No blog depurável, classifique 3 para as palavras-chave "Exceção não capturada do Node.JS")
Número4: manipular erros centralmente, através, mas não no middleware
TL; DR: A lógica de manipulação de erros, como correio para administrador e log, deve ser encapsulada em um objeto dedicado e centralizado que todos os terminais (por exemplo, middleware Express, tarefas cron, teste de unidade) chamam quando ocorre um erro.
Caso contrário: não manipular erros em um único local levará à duplicação de código e provavelmente a erros tratados incorretamente
Exemplo de código - um fluxo de erro típico
Citação do blog: "Às vezes, níveis mais baixos não podem fazer nada de útil, exceto propagar o erro ao chamador" (no blog Joyent, classificou 1 para as palavras-chave "Tratamento de erros do Node.JS")
Número5: documentar erros da API usando o Swagger
TL; DR: informe aos chamadores da API quais erros podem resultar em retorno, para que eles possam lidar com eles com cuidado sem travar. Isso geralmente é feito com estruturas de documentação da API REST como o Swagger
Caso contrário: um cliente de API pode decidir travar e reiniciar apenas porque recebeu de volta um erro que não conseguia entender. Nota: o chamador da sua API pode ser você (muito típico em um ambiente de microsserviços)
Citação do blog: "Você precisa informar aos chamadores quais erros podem ocorrer" (no blog Joyent, classificado em 1 pelas palavras-chave “Registro no Node.JS”)
Número6: Encerre o processo normalmente quando um estranho chegar à cidade
TL; DR: Quando ocorre um erro desconhecido (um erro de desenvolvedor, consulte a prática recomendada número 3) - há incerteza sobre a integridade do aplicativo. Uma prática comum sugere reiniciar o processo com cuidado usando uma ferramenta 'restarter' como Forever e PM2
Caso contrário: Quando uma exceção desconhecida é capturada, algum objeto pode estar com um estado defeituoso (por exemplo, um emissor de evento que é usado globalmente e não dispara mais eventos devido a alguma falha interna) e todas as solicitações futuras podem falhar ou se comportar loucamente
Exemplo de código - decidindo se deve travar
Citação do blog: "Existem três pensamentos sobre o tratamento de erros" (do blog jsrecipes)
Número7: Use um criador de logs maduro para aumentar a visibilidade dos erros
TL; DR: Um conjunto de ferramentas de registro maduras, como Winston, Bunyan ou Log4J, acelerará a descoberta e o entendimento de erros. Então esqueça o console.log.
Caso contrário: percorrer o console.logs ou manualmente o arquivo de texto confuso, sem consultar as ferramentas ou um visualizador de logs decente, poderá mantê-lo ocupado no trabalho até tarde
Exemplo de código - Winston logger em ação
Citação do blog: "Vamos identificar alguns requisitos (para um criador de logs):" (Do blog forte do blog)
Número8: Descubra erros e tempo de inatividade usando produtos APM
TL; DR: os produtos de monitoramento e desempenho (também conhecidos como APM) medem proativamente sua base de código ou API para que possam destacar automaticamente magicamente erros, falhas e partes lentas que estavam faltando
Caso contrário: você pode se esforçar bastante para medir o desempenho e os tempos de inatividade da API, provavelmente nunca saberá quais são as partes de código mais lentas no cenário do mundo real e como isso afeta o UX
Citação do blog: "segmentos de produtos APM" (do blog Yoni Goldberg)
A descrição acima é uma versão reduzida - veja aqui mais práticas recomendadas e exemplos
fonte
Você pode capturar exceções não capturadas, mas é de uso limitado. Consulte http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb
monit
,forever
ouupstart
pode ser usado para reiniciar o processo do nó quando ele falha. Um desligamento normal é o melhor que você pode esperar (por exemplo, salve todos os dados na memória no manipulador de exceções não capturado).fonte
Error
torna polimórfico o valor de retorno que atrapalha desnecessariamente a semântica da função. Além disso, por mergulho 0 já é tratado no JavaScript, dandoInfinity
,-Infinity
, ouNaN
, se for caso valorestypeof === 'number'
. Eles podem ser verificados com!isFinite(value)
. Em geral, eu recomendaria nunca retornar um erro de uma função. Melhor em termos de legibilidade e manutenção do código, para lançar ou retornar um valor não polimórfico especial com semântica consistente.Os domínios do nodejs são a maneira mais atualizada de lidar com erros no nodejs. Os domínios podem capturar eventos de erro / outros, bem como objetos lançados tradicionalmente. Os domínios também fornecem funcionalidade para lidar com retornos de chamada com um erro passado como o primeiro argumento por meio do método de interceptação.
Como na manipulação normal de erros no estilo try / catch, geralmente é melhor lançar erros quando eles ocorrem e bloquear áreas nas quais você deseja isolar os erros de afetar o restante do código. A maneira de "bloquear" essas áreas é chamar domain.run com uma função como um bloco de código isolado.
No código síncrono, o acima é suficiente - quando ocorre um erro, você o deixa passar, ou o pega e lida com ele, revertendo todos os dados necessários.
Quando o erro ocorre em um retorno de chamada assíncrono, você precisa ser capaz de lidar totalmente com a reversão de dados (estado compartilhado, dados externos como bancos de dados, etc.). OU você deve definir algo para indicar que ocorreu uma exceção - sempre que você se importar com esse sinalizador, precisará aguardar a conclusão do retorno de chamada.
Alguns dos códigos acima são feios, mas você pode criar padrões para torná-lo mais bonito, por exemplo:
ATUALIZAÇÃO (2013-09):
Acima, eu uso um futuro que implica semântica de fibras , o que permite que você espere futuros em linha. Na verdade, isso permite que você use blocos tradicionais de try-catch para tudo - o que eu acho o melhor caminho a percorrer. No entanto, você nem sempre pode fazer isso (ou seja, no navegador) ...
Também existem futuros que não requerem semântica de fibras (que funcionam com JavaScript normal de navegação). Estes podem ser chamados de futuros, promessas ou diferidos (vou me referir a futuros daqui em diante). As bibliotecas de futuros JavaScript simples e antigos permitem que os erros sejam propagados entre futuros. Somente algumas dessas bibliotecas permitem que qualquer futuro lançado seja manipulado corretamente, portanto, tenha cuidado.
Um exemplo:
Isso imita um try-catch normal, mesmo que as peças sejam assíncronas. Seria impresso:
Observe que ele não imprime '3' porque foi lançada uma exceção que interrompe esse fluxo.
Dê uma olhada nas promessas do bluebird:
Observe que não encontrei muitas outras bibliotecas além dessas que lidam adequadamente com exceções lançadas. O jQuery é adiado, por exemplo, não - o manipulador "fail" nunca receberia a exceção de um manipulador a 'then', o que, na minha opinião, é um rompimento de negócio.
fonte
Eu escrevi sobre isso recentemente em http://snmaynard.com/2012/12/21/node-error-handling/ . Um novo recurso do nó na versão 0.8 é domínios e permite combinar todas as formas de tratamento de erros em um formulário de gerenciamento mais fácil. Você pode ler sobre eles no meu post.
Você também pode usar algo como o Bugsnag para rastrear suas exceções não capturadas e ser notificado por e-mail, sala de bate-papo ou criar um ticket para uma exceção não capturada (eu sou o co-fundador da Bugsnag).
fonte
Gostaria apenas de adicionar que a biblioteca Step.jx ajuda a lidar com exceções, sempre passando-a para a próxima etapa. Portanto, você pode ter como último passo uma função que verifica se há erros em qualquer uma das etapas anteriores. Essa abordagem pode simplificar bastante o tratamento de erros.
Abaixo está uma citação da página do github:
Além disso, você pode usar o Step para controlar a execução de scripts para ter uma seção de limpeza como o último passo. Por exemplo, se você deseja escrever um script de construção no Node e relatar quanto tempo levou para escrever, a última etapa pode fazer isso (em vez de tentar descobrir o último retorno de chamada).
fonte
Uma instância em que o uso de um try-catch pode ser apropriado é o uso de um loop forEach. É síncrono, mas ao mesmo tempo você não pode usar apenas uma declaração de retorno no escopo interno. Em vez disso, uma abordagem try and catch pode ser usada para retornar um objeto Error no escopo apropriado. Considerar:
É uma combinação das abordagens descritas por @balupton acima.
fonte
Depois de ler este post há algum tempo, fiquei pensando se era seguro usar domínios para manipulação de exceções em um nível de API / função. Eu queria usá-los para simplificar o código de manipulação de exceções em cada função assíncrona que escrevi. Minha preocupação era que o uso de um novo domínio para cada função introduzisse uma sobrecarga significativa. Minha lição de casa parece indicar que há uma sobrecarga mínima e que o desempenho é realmente melhor com domínios do que com try catch em algumas situações.
http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/
fonte
A captura de erros foi muito bem discutida aqui, mas vale lembrar de registrar os erros em algum lugar para que você possa vê-los e consertar as coisas.
Bunyan é uma estrutura de log popular para o NodeJS - ela suporta escrever em vários locais de saída diferentes, o que a torna útil para depuração local, desde que você evite o console.log. No manipulador de erros do seu domínio, você pode cuspir o erro em um arquivo de log.
Isso pode consumir muito tempo se você tiver muitos erros e / ou servidores para verificar, então pode valer a pena procurar uma ferramenta como Raygun (aviso de isenção de responsabilidade, trabalho na Raygun) para agrupar erros - ou usá-los juntos. Se você decidiu usar o Raygun como uma ferramenta, é muito fácil configurar também
Além de usar uma ferramenta como o PM2 ou para sempre, seu aplicativo deve travar, desconectar o que aconteceu e reiniciar sem grandes problemas.
fonte
Se você deseja usar os Serviços no Ubuntu (Upstart): Nó como um serviço no Ubuntu 11.04 com upstart, monit e forever.js
fonte