Sentry-javascript: Sugestão de nó: registrar erros em `unhandledRejection`

Criado em 19 fev. 2019  ·  41Comentários  ·  Fonte: getsentry/sentry-javascript

Pacote + Versão

  • [] @sentry/browser
  • [x] @sentry/node
  • [] raven-js
  • [] raven-node _ (raven para o nó) _
  • [ ] de outros:

Versão:

4.6.1

Descrição

Pronto para uso, o Node registrará rejeições de promessa não tratadas. No entanto, após inicializar o Sentry, esses logs desaparecerão. Isso acontece porque o Node não registrará rejeições se um manipulador unhandledRejection existir e o Sentry registrar tal manipulador.

Eu gostaria de sugerir que o Sentry adicione o registro ao seu manipulador, para fornecer paridade com a experiência fornecida fora da caixa. No mínimo, os documentos devem mencionar esse comportamento e a necessidade de adicionar manualmente um manipulador adicional para restaurar o registro.

Comentários muito úteis

Ainda acho estranho que uncaughtException e unhandledRejection sejam tratados de forma diferente. O Sentinela restaura o registro de uncaughtException —por que não faz o mesmo para unhandledRejection ? Os usuários não devem se lembrar de usar esse sinalizador de Nó. 🤔

Todos 41 comentários

Parece bom, no entanto, não tenho certeza se isso deve estar por trás de nossa bandeira debug: true .
No final, você está conectando conscientemente manipuladores de erros, que redirecionam o fluxo de erros para o Sentry.

Eu também não tenho certeza.

Habilitar Sentry não tem o efeito de desabilitar o registro para exceções não capturadas, então talvez o mesmo deva ser verdade para rejeições de promessa não tratadas. (Embora essa diferença de comportamento seja mais uma inconsistência no Node do que no Sentry.)

Quando eu habilito o Sentry, entendo que ele está relatando meus erros, mas tendo a pensar no log do console como uma coisa separada, especialmente no dev. 🤔

Não modificamos o comportamento de registro padrão e, para exceções não detectadas, é o próprio nó que está acionando uma chamada de registro.

Acho que a melhor solução aqui seria apenas anotar isso na documentação e sair

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at:', p, 'reason:', reason);
});

snippet no caso de alguém querer reativar o aviso.

Os documentos seriam suficientes. Pelo menos o Sentinela não está fazendo nada de especial. É realmente o Node que é o problema aqui.

ou exceções não capturadas é o próprio nó que está acionando uma chamada de log.

Pensando bem, não tenho certeza se isso é verdade.

O Sentry registra um manipulador uncaughtException que desabilita o comportamento padrão do Node (log + saída).

O manipulador ( defaultOnFatalError ) aciona sua própria chamada de registro: https://github.com/getsentry/sentry-javascript/blob/4.6.3/packages/node/src/handlers.ts#L282.

Se estivermos "restaurando" o registro para exceções não detectadas, acredito que devemos fazer a mesma coisa para rejeições de promessa não tratadas.

Podemos reabrir isso? De acordo com meu último comentário, agora acredito que o Sentry deve restaurar o registro para rejeições de promessa não tratadas, como faz para exceções não capturadas.

Eu encontrei esse problema porque eu estava começando a usar o Sentry e me perguntei se eu tinha cometido um erro no meu código, já que apenas uncaughtException estava em meu log e não unhandledRejection . Faria muito sentido que esses dois casos fossem tratados da mesma maneira.

Então, eu diria registrar ambos ou registrar nenhum.

Eu encontrei uma maneira melhor de lidar com isso, do que copiar o código do núcleo do Node.

λ: node --unhandled-rejections=warn app.js

Também ficou óbvio ao incluir na página de documentos principal https://github.com/getsentry/sentry-docs/pull/1099

Ainda acho estranho que uncaughtException e unhandledRejection sejam tratados de forma diferente. O Sentinela restaura o registro de uncaughtException —por que não faz o mesmo para unhandledRejection ? Os usuários não devem se lembrar de usar esse sinalizador de Nó. 🤔

por que não faz o mesmo para a rejeição não tratada

Porque um é crítico ( unhandledException mata o processo) e outro é informativo (portanto, é um aviso, não um erro).

Se invertermos a ordem e emitirmos o aviso por padrão, quebraremos o comportamento da CLI, emitindo-os apesar de --unhandled-rejections estar definido como none . No momento, tudo funciona conforme o esperado de acordo com a documentação oficial do nó. Esta mudança _tornaria_ fora do padrão.

Assim que o Node decidir fazer com que unhandledRejection também mate um processo (o que eles dizem para 4 versões agora: P), faremos a alteração para estar em linha com a especificação oficial também.

Se invertermos a ordem e emitirmos o aviso por padrão, quebraremos o comportamento da CLI

@kamilogorek , mas ao executar com node ele registra unhandledRejection em console . Portanto, não sei o que diz a documentação oficial do nó, mas pelo menos é o comportamento que percebo.

@freeall somente se você não anexar unhandledRejection handler. Se você executar o código abaixo, mesmo sem o SDK, ele ainda não irá registrar, portanto, você deve estar ciente de qual código está executando.

process.on('unhandledRejection', () => `¯\_(ツ)_/¯`);

E declaramos muito claro aqui que fazemos isso: https://docs.sentry.io/platforms/node/default-integrations/

As integrações de sistema são integrações habilitadas por padrão que se combinam na biblioteca padrão ou no próprio intérprete. Eles estão documentados para que você possa ver o que eles fazem e que podem ser desativados se causarem problemas.
OnUnhandledRejection
Essa integração anexa manipuladores globais de rejeição não tratada.

Eu só não quero mudar o comportamento do nó, é isso. E o comportamento é: "se houver um ouvinte, não emita. Se você ainda quiser o aviso, use o sinalizador." - e isso é exatamente o que fazemos.

@kamilogorek Eu entendo de onde você vem. Mas eu acho que os usuários do Sentry esperam que o Sentry não altere o comportamento de seu programa.

Se eu tiver um unhandledRejection sem Sentinela, eu o vejo em meu console.
Se eu tiver um unhandledRejection com o Sentinela, não o vejo em meu console.

Eu pessoalmente não gosto que o Sentinela altere o comportamento.

Mas é assim que o Node.js é projetado ¯_ (ツ) _ / ¯
Se você adicionar o manipulador, o aviso desaparece. Nosso SDK adiciona handler, porque é a única maneira de detectar erros não tratados, que é o objetivo principal do SDK.

É claro que você está certo sobre como o nó é projetado. Quando você anexa um manipulador, o aviso desaparece.
O que as pessoas estão pedindo é que você imite o comportamento padrão do nó e registre-o no console. Um comportamento alterado não é o que você espera de uma ferramenta como o Sentry

De qualquer forma, parece que você está decidido a seguir esse comportamento, então não adianta continuar a discussão. Mas obrigado por responder :)

@freeall obrigado também, é sempre bom ver os dois lados :)

Só para esclarecer: ao habilitar o Sentry, o comportamento de unhandledException (exit + log) é preservado, mas o comportamento de unhandledRejection (log) não é:

| manipulador | logs | saídas |
| - | - | - |
| unhandledException default | yes | yes |
| unhandledException Sentinela | sim | sim |
| unhandledRejection default | sim | não |
| unhandledRejection Sentinela | no | no |

No momento, tudo funciona conforme o esperado de acordo com a documentação oficial do nó.

"Como esperado" aqui está assumindo que o usuário entende que o Sentry está registrando um ouvinte para unhandledRejection . Esse é um detalhe de implementação com o qual os usuários não devem se preocupar.

Eu entendo seu ponto de vista, no entanto. O Sentinela também deve respeitar --unhandled-rejections , o que não aconteceria se o sinalizador fosse definido como none e o Sentinela continuasse a registrar.

O comentário

Acho que os usuários do Sentry esperam que o Sentry não altere o comportamento de seu programa.

Rejeições de promessa não detectadas não são "erros de segunda classe". Eles podem causar falhas nos aplicativos, como os erros normais fazem.

Parece que vários usuários se depararam com esse problema (inclusive eu), e muitos outros vão se deparar com ele no futuro.

Resumindo: o Sentry silencia os erros do console. E eu acho que é óbvio como isso é confuso, e provavelmente não foi pretendido pela maioria dos usuários de sentinela. Eles não instalam o sentry para que algum subconjunto de erros seja silenciado no console.

Então, o raciocínio (por @kamilogorek )

é assim que o Node.js é projetado

É um pouco enigmático para mim. Como isso dita a maneira como o sentinela deve se comportar?

Devemos esperar que os usuários saibam sobre:

a) funcionamentos internos sentrys (registra manipuladores de eventos)

b) funcionamento interno do nó (adicionar manipuladores faz com que os avisos desapareçam)

E se não o fizerem, bem, eles deveriam?

Claro que você está certo ao dizer "bem, o usuário deve saber o que o código faz nos bastidores", mas a realidade parece diferente. E você tem a oportunidade aqui de aumentar a facilidade de uso do sentinela, ou não.

É normal dizer "não nos importamos com esse problema específico", mas pintá-lo de uma forma "não é um bug, é um recurso" parece insincero.

TLDR: IMOH Se você deseja aumentar a facilidade de uso e reduzir os problemas para usuários novatos, isso deve ser corrigido.

@OliverJAsh , @freeall
Que soluções você usou para este problema?

@schumannd adicionei um trecho de como o carrego. E concordo com você que a resposta "não é um bug, é um recurso" não parece satisfatória. Temo que muitos programadores não detectem alguns bugs em seus programas porque o Sentry os come. Para mim, a primeira prioridade de uma ferramenta como o Sentry deve ser pegar bugs, e não criá-los.

...
if (isUsingSentry) {
  // Log to console because Sentry overwrites standard behavior. https://github.com/getsentry/sentry-javascript/issues/1909.
  // Note that it doesn't overwrite for uncaughtException.
  process.on('unhandledRejection', console.error)
}
...

Como estou usando o sentinela como nativo de reação, parece que preciso fazer algumas coisas hacky para corrigir esse problema. Alguém está usando o React Native e resolvendo esse problema de uma maneira diferente?

@OliverJAsh , @kamilogorek
Podemos reabrir e consertar isso?

O Sentry não deve mexer em como os erros são registrados no console - e isso não é apenas um problema com o Node, já que o registro do console também é suprimido no navegador.

É muito irritante tentar depurar coisas, e como você não se preocupou em mencionar isso na documentação do JavaScript, na verdade pensei que não tinha erros quando olhei no console enquanto testava uma compilação de teste e, portanto, não percebi. teve erros, até depois de ter sido implantado em produção. Essa é uma experiência de usuário realmente péssima para um serviço de relatório de erros e que você precisa consertar se quiser clientes satisfeitos.

E, como disse antes, rejeições de promessa não são erros de segunda classe - elas podem ser tão fatais quanto qualquer outro erro não tratado e não devem ser suprimidas de nenhuma forma.

Portanto, um pouco de depuração revela o motivo pelo qual o registro do console foi suprimido:

image

O Sentinela atribui uma função a window.onunhandledrejection e, como vemos aqui, essa função retorna false , suprimindo explicitamente o log do console. Então sim, o Sentry _muda_ o comportamento padrão - isso não é legal.

Felizmente, ele armazena uma referência a qualquer função existente e a chama, se houver.
Portanto, a solução alternativa para reativar o registro do console é adicionar esta linha, antes de inicializar o Sentry:

window.onunhandledrejection = () => true;

Agora, por favor, corrija isso, para que possamos ter o comportamento padrão sem esses hacks inúteis 🙂

@schumannd por favor reabra o problema

@ thomas-darling eu concordo com a mudança do navegador, ele deve retornar true para promessas também e eu posso mudar isso.

No entanto, para o nó, ainda não estou convencido por um motivo. Ele vincula o código à implementação do Node atual. Se copiarmos os internos em vez de depender de sinalizadores e prometer que o comportamento de rejeição mudará na v14, teremos que detectar em qual versão do nó estamos e agir de acordo.
Não importa o que retornamos do ouvinte, já que internamente o node apenas verifica a matriz de ouvintes e emite um aviso apenas se não houver ouvintes, e essa detecção não pode ser modificada - https://github.com/nodejs /node/blob/7cf6f9e964aa00772965391c23acda6d71972a9a/lib/internal/process/promises.js#L163 -L216

Parece bom, em relação à mudança do navegador: +1:

Quanto ao Node, se você não consertar o registro no Sentry, você basicamente apenas força todos os seus usuários a fazerem sozinhos - com o risco adicional de que alguns façam errado, e alguns nem percebam que precisam disso, até depois que ser picado por um bug de produção, como eu. Essa não é uma boa experiência de desenvolvedor ...

@ thomas-querido, como você gostaria de consertá-lo? Reproduzir o mesmo código que está dentro do código do nó?

Bem no topo de nossos documentos, há uma observação muito visível sobre o que fazer para obter o registro de console padrão - https://docs.sentry.io/platforms/node/

image

Eu entendo seu ponto - ter que replicar o comportamento do nó seria um problema de manutenção potencial e ajuda que isso possa, para o nó, ser resolvido com um sinalizador de linha de comando simples.

Mas se você não quiser replicar o comportamento do nó, então pelo menos registre um aviso no console quando esse sinalizador não for especificado, para que os usuários fiquem cientes de que os erros estão sendo suprimidos e como evitar isso.

Isso se torna ainda mais importante na próxima versão do nó, onde rejeições não tratadas irão, por padrão, travar o processo - o que, pelo que entendi, não acontecerá quando o Sentry adicionar seu manipulador.
Os usuários que contam com esse novo comportamento padrão no nó, podem ter uma surpresa desagradável, se mais tarde instalarem o Sentry e seu processo continuar repentinamente, apesar de um erro fatal ter ocorrido.
Esse é o tipo de coisa que pode levar à perda de dados ou outros desastres.

A meu ver, existem algumas opções:

  1. Replicar a maneira como o Node.js faz isso
  2. Basta escrever para console.error quando houver uma rejeição não tratada
  3. Suprima o erro para que o desenvolvedor nunca o veja

Acho que as opções 1 ou 2 parecem boas. Seu cliente vê o erro e pode corrigi-lo.
O que você absolutamente não deve fazer é a opção 3, onde seu cliente não vê erros e o Sentry faz com que os erros entrem em produção (oh, mas a ironia de uma ferramenta de relatório de erros). Este é o comportamento atual e isso realmente deveria parar! O Sentinela deve me ajudar a detectar erros, não torná-los piores.

Mesmo se você escolher a opção 2, pelo menos os desenvolvedores verão a rejeição e perceberão que desejam um comportamento diferente (como uma falha) e podem implementá-lo. Mas, mesmo sem saber que houve uma rejeição, eles não podem fazer muito a respeito.

Isso deve fazer o trabalho. https://github.com/getsentry/sentry-javascript/pull/2312
Não adicionei uma maneira de adicionar seu próprio retorno de chamada, pois escrever o código abaixo teria exatamente o mesmo efeito:

`` `js
Sentry.init ({
integrações: [
new Sentry.Integrations.OnUnhandledRejection ({
modo: 'nenhum'
})
]
});

process.on ('unhandledRejection', (motivo) => {
// seu retorno de chamada
})

para mim, isso resulta em TypeError: undefined is not a constructor . Pode ser, porque agora estou usando o pacote @sentry/react-native . Btw esse pacote tem o mesmo problema?

@schumannd @sentry/react-native não usa @sentry/node , então não tem essa integração. Para isso, você só precisará atualizar uma versão assim que lançarmos o sentry / navegador e funcionará bem (já que a mudança para retornar true dos manipuladores é padrão e não configurável).

@kamilogorek parece bom para mim 👍

Obrigado por fazer isso! Você conseguiria pingar aqui quando isso for lançado?

@OliverJAsh ping :)

Só para verificar se entendi corretamente: se eu usar o sinalizador --unhandled-rejections=strict Node, o Node levantará a rejeição não tratada como uma exceção e o Sentry irá interceptar essa exceção e relatá-la? É isso que acho que estou vendo.

Pergunto porque quando tentei habilitar --unhandled-rejections=strict , parecia que a integração OnUnhandledRejections não tinha efeito - o ouvinte de evento nunca foi chamado.

Seria ótimo se pudéssemos adicionar alguns documentos sobre isso!

O Docs PR já está em andamento https://github.com/getsentry/sentry-docs/pull/1351/

@OliverJAsh esta mudança não tem nada a ver com o sinalizador cli. Seu comportamento permanece intocado. A única coisa que mudou é que a integração OnUnhandledRejection ganhou uma nova opção que permite que você se comporte como o sinalizador cli.

Sentry.init({
  integrations: [
    new Sentry.Integrations.OnUnhandledRejection({
      mode: 'none'
    })
  ]
});

é (conceitualmente) o mesmo que --unhandled-rejection=none e o mesmo vale para warn e strict .
Quando você usa warn (que é o padrão agora), ele registrará o aviso e o erro, mas o processo será mantido ativo.
Quando você usa strict , ele irá registrar, capturar o evento, liberá-lo (espere até que seja entregue) e encerrar o processo com o código de saída 1.

Isso faz sentido. Eu entendo que a integração não muda o comportamento do sinalizador do Node. No entanto, posso apenas verificar se entendi corretamente como o Sentry se comporta (fora dessa integração) em relação ao sinalizador de Nó?

Apenas para verificar se entendi corretamente: se eu usar o sinalizador --unhandled-overrions = strict do Node, o Node levantará a rejeição não tratada como uma exceção e, em seguida, o Sentry irá interceptar essa exceção e relatá-la? É isso que acho que estou vendo.

@schumannd FYI, esta manhã lançamos @sentry/react-native 1.10.0 [EDIT: ops, deve ser 1.1.0 ], que atualiza sua dependência para usar a versão mais recente de @sentry/browser (que inclui o retorno - true -em vez de- false correção mencionada acima).

@lobsterkatie a última versão de @ sentry / parece ser 1.3.7. .

Portanto, tentar instalar o 1.10.0 não funciona. Como faço para obter a correção?

@schumannd @lobsterkatie significava 1.1.0 , pois foi quando atualizamos para 5.9.0 de @sentry/browser . A opção do manipulador que define o nível de registro deve funcionar bem na versão recente de @sentry/react-native também.

Esta página foi útil?
0 / 5 - 0 avaliações