O material, cuja tradução publicamos hoje, é dedicado ao tratamento de erros de JS usando
window.onerror
. Este é um evento especial do navegador que é acionado quando ocorrem erros não detectados. Aqui, falaremos sobre como detectar erros usando o
onerror
eventos
onerror
e como enviar informações sobre eles ao servidor do desenvolvedor do site. Esse manipulador pode ser usado como base do seu próprio sistema para coletar e analisar informações de erro. Além disso, é um dos mecanismos mais importantes usados em bibliotecas orientadas a erros, como
raven-js .

Escutando o evento window.onerror
Você pode ouvir o evento
onerror
atribuindo
window.onerror
função que desempenha o papel de um manipulador de erros:
window.onerror = function(msg, url, lineNo, columnNo, error) { // ... ... return false; }
Essa função é chamada quando ocorre um erro; os seguintes argumentos são passados para ela:
msg
- mensagem de erro. Por exemplo, Uncaught ReferenceError: foo is not defined
.url
- o endereço do script ou documento em que o erro ocorreu. Por exemplo, /dist/app.js
.lineNo
- número da linha onde ocorreu o erro (se suportado).columnNo
- o número da coluna da linha (se suportado).error
- o objeto de error
(se suportado).
Os quatro primeiros argumentos informam ao desenvolvedor qual script, em qual linha e em qual coluna dessa linha ocorreu um erro. O argumento final, um objeto do tipo
Error
, é talvez o mais importante de todos os argumentos. Vamos conversar sobre isso.
Objeto de erro e propriedade Error.prototype.stack
À primeira vista, o objeto
Error
não é nada de especial. Ele contém três propriedades bastante padrão -
message
,
fileName
e
lineNumber
. Esses dados, dadas as informações transmitidas ao manipulador de eventos
window.onerror
, podem ser considerados redundantes.
O valor real nesse caso é a propriedade não padrão
Error.prototype.stack
. Essa propriedade fornece acesso à pilha de chamadas (pilha de erros), permite descobrir o que estava acontecendo no programa no momento em que ocorreu o erro, cuja chamada de função precedeu sua aparência. O rastreamento da pilha de chamadas pode ser uma parte crítica do processo de depuração. E, apesar de a propriedade
stack
não ser padrão, ela está disponível em todos os navegadores modernos.
É assim que a propriedade
stack
do objeto de erro se parece no Chrome 46.
"Error: foobar\n at new bar (<anonymous>:241:11)\n at foo (<anonymous>:245:5)\n at <anonymous>:250:5\n at <anonymous>:251:3\n at <anonymous>:267:4\n at callFunction (<anonymous>:229:33)\n at <anonymous>:239:23\n at <anonymous>:240:3\n at Object.InjectedScript.\_evaluateOn (<anonymous>:875:140)\n at Object.InjectedScript.\_evaluateAndWrap (<anonymous>:808:34)"
À nossa frente está uma string não formatada. Quando o conteúdo desta propriedade é apresentado neste formulário, é inconveniente trabalhar com ela. Veja como o mesmo ficará após a formatação.
Error: foobar at new bar (<anonymous>:241:11) at foo (<anonymous>:245:5) at callFunction (<anonymous>:229:33) at Object.InjectedScript._evaluateOn (<anonymous>:875:140) at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34)
Agora, após a formatação, a pilha de erros parece muito mais clara, imediatamente fica claro por que a propriedade da
stack
é muito importante ao depurar erros.
No entanto, aqui tudo não está indo bem. A propriedade de
stack
não é padronizada, é implementada de maneira diferente em navegadores diferentes. Aqui está, por exemplo, a aparência da pilha de erros no Internet Explorer 11.
Error: foobar at bar (Unknown script code:2:5) at foo (Unknown script code:6:5) at Anonymous function (Unknown script code:11:5) at Anonymous function (Unknown script code:10:2) at Anonymous function (Unknown script code:1:73)
Você pode ver, em comparação com o exemplo anterior, que aqui não apenas um formato diferente para representar quadros de pilha é usado, mas também que há menos dados para cada quadro. Por exemplo, o Chrome identifica instâncias do uso da
new
palavra-chave e fornece informações mais detalhadas sobre outros eventos (em particular, sobre chamadas de função.
_evaluateOn
e.
_evaluateAndWrap
). Ao mesmo tempo, aqui comparamos apenas o que o IE e o Chrome divulgam. Outros navegadores usam suas próprias abordagens para exibir dados sobre a pilha e selecionar as informações incluídas nesses dados.
Para trazer tudo isso para uma aparência uniforme, você pode usar ferramentas de terceiros. Por exemplo, o raven-js usa o TraceKit para isso. Stacktrace.js e alguns outros projetos atendem ao mesmo objetivo.
Recursos do suporte window.onerror por vários navegadores
O evento
windows.onerror
existe nos navegadores há algum tempo. Em particular, ele pode ser encontrado no IE6 e no Firefox 2. O problema aqui é que todos os navegadores implementam o
windows.onerror
maneiras diferentes. Por exemplo, isso diz respeito ao número e estrutura dos argumentos transmitidos aos manipuladores deste evento.
Aqui está uma tabela que
onerror
argumentos passados para o manipulador
onerror
nos principais navegadores.
Navegador
| mensagem
| url
| lineNo
| colNo
| errorObj
|
Firefox
| Existe
| Existe
| Existe
| Existe
| Existe
|
Chrome
| Existe
| Existe
| Existe
| Existe
| Existe
|
Edge
| Existe
| Existe
| Existe
| Existe
| Existe
|
IE 11
| Existe
| Existe
| Existe
| Existe
| Existe
|
IE10
| Existe
| Existe
| Existe
| Existe
| Não
|
IE 9.8
| Existe
| Existe
| Existe
| Não
| Não
|
Safari 10 e superior
| Existe
| Existe
| Existe
| Existe
| Existe
|
Safari 9
| Existe
| Existe
| Existe
| Existe
| Não
|
Navegador Android 4.4
| Existe
| Existe
| Existe
| Existe
| Não
|
Provavelmente, não é de surpreender que o Internet Explorer 8, 9 e 10 tenham suporte limitado ao
onerror
. No entanto, pode parecer incomum que no navegador Safari o suporte para o objeto de erro apareça apenas na 10ª versão, lançada em 2016. Além disso, existem dispositivos móveis herdados que usam o navegador Android padrão, que também não suporta o objeto de erro. Nas versões modernas do Android, este navegador foi substituído pelo Chrome Mobile.
Se não houver nenhum objeto de erro à nossa disposição, não haverá dados sobre o rastreamento da pilha. Isso significa que navegadores que não oferecem suporte ao objeto do erro não fornecem informações de pilha no script padrão para usar o manipulador
onerror
. E isso, como dissemos, é muito importante.
Desenvolvimento de polyfill para window.onerror usando a construção try / catch
Para obter informações sobre a pilha em navegadores que não oferecem suporte à passagem de um objeto de
onerror
para o manipulador
onerror
, você pode usar o seguinte truque. Você pode agrupar o código em uma construção
try/catch
e capturar os erros você mesmo. O objeto de erro resultante conterá, em todos os navegadores modernos, o que precisamos é da propriedade
stack
.
Dê uma olhada no código do método auxiliar
invoke()
, que chama o método especificado do objeto, passando a ele uma matriz de argumentos.
function invoke(obj, method, args) { return obj[method].apply(this,args); }
Veja como usá-lo.
invoke(Math, 'max', [1,2])
Aqui está o mesmo
invoke()
, mas agora a chamada do método é agrupada em
try/catch
, o que permite capturar possíveis erros.
function invoke(obj, method, args) { try { return obj[method].apply(this,args); } catch(e) { captureError(e);// throw e;// } } invoke(Math,'highest',[1,2]); // , Math.highest
Obviamente, é muito caro adicionar essas estruturas manualmente a todos os locais onde elas possam ser necessárias. Essa tarefa pode ser simplificada criando uma função auxiliar universal.
function wrapErrors(fn) { // if(!fn.__wrapped__) { fn.__wrapped__ = function() { try{ return fn.apply(this,arguments); }catch(e){ captureError(e);// throw e;// } }; } return fn.__wrapped__; } var invoke = wrapErrors(function(obj, method, args) { returnobj[method].apply(this,args); }); invoke(Math,'highest',[1,2]);//, Math.highest
Como o JavaScript usa um modelo de execução de código de thread único, esse wrapper deve ser usado apenas com as chamadas de função que estão no início de novas pilhas. Não há necessidade de agrupar todas as chamadas de função nele.
Como resultado, verifica-se que essa função precisa ser usada nos seguintes locais:
- Onde o aplicativo é iniciado (por exemplo, ao usar jQuery, na função
$(document).ready
) - Nos manipuladores de eventos (por exemplo, em
addEventListener
ou em construções no formato $.fn.click
) - Nos retornos de chamada chamados por eventos de timer (por exemplo, é
setTimeout
ou requestAnimationFrame
)
Aqui está um exemplo usando a função
wrapErrors
.
$(wrapErrors(function () {// doSynchronousStuff1();// setTimeout(wrapErrors(function () { doSynchronousStuff2();// })); $('.foo').click(wrapErrors(function () { doSynchronousStuff3();// })); }));
Essas construções podem ser adicionadas ao código você mesmo, mas isso é uma tarefa muito demorada. Como alternativa conveniente nessas situações, você pode considerar as bibliotecas para trabalhar com erros, que, por exemplo, possuem mecanismos que
addEventListener
e
setTimeout
ferramentas para
addEventListener
erros.
Erro na transferência para o servidor
Portanto, agora temos à nossa disposição meios de interceptar informações de erro usando o
windows.onerror
ou funções auxiliares baseadas em
try/catch
. Esses erros ocorrem no lado do cliente e, após sua interceptação, gostaríamos de lidar com eles e tomar medidas para eliminá-los. Para fazer isso, eles precisam ser transferidos para o nosso servidor. Para fazer isso, você precisa preparar um serviço da Web que receba informações sobre erros via HTTP e, de alguma forma, salvá-los para processamento adicional, digamos - gravaria em um arquivo de log ou banco de dados.
Se este serviço da web estiver localizado no mesmo domínio que o aplicativo da web,
XMLHttpRequest
será suficiente. O exemplo a seguir mostra como usar uma função para executar consultas AJAX do jQuery para transferir dados para um servidor.
function captureError(ex){ var errorData = { name:ex.name,// : ReferenceError message:ex.line,// : x is undefined url:document.location.href, stack:ex.stack// ; , ! }; $.post('/logger/js/',{ data:errorData }); }
Lembre-se de que, se você precisar enviar solicitações entre domínios para enviar informações sobre erros ao servidor, será necessário dar suporte a essas solicitações.
Sumário
Você aprendeu o básico da criação de um serviço para capturar erros e enviar informações sobre eles ao servidor. Em particular, aqui examinamos os seguintes problemas:
- Recursos do evento
onerror
e seu suporte em vários navegadores. - Usando o mecanismo
try/catch
para obter informações sobre a pilha de chamadas nos casos em que o onerror
não oferece suporte ao trabalho com o objeto de erro. - Transfira os dados do erro para o servidor do desenvolvedor.
Tendo aprendido sobre como os mecanismos acima funcionam, você adquiriu conhecimentos básicos que lhe permitirão começar a criar seu próprio sistema para trabalhar com erros, esclarecendo detalhes adicionais durante o trabalho. Talvez esse cenário seja especialmente relevante para os casos em que se trata de um aplicativo em que, digamos, por motivos de segurança, não está planejado o uso de bibliotecas de terceiros. Se o seu aplicativo permitir o uso de código de terceiros, você poderá encontrar muito bem a ferramenta certa para monitorar erros de JS. Entre essas ferramentas estão
Sentry ,
Rollbar ,
TrackJS e outros projetos similares.
Caros leitores! Quais ferramentas para monitorar erros de JS você usa?
