ReportingObserver: conheça a integridade do código

Texto longo, leia o resumo

Tem um novo observador na cidade! A ReportingObserver é uma nova API que permite saber quando seu site usa uma API descontinuada ou é executado em uma intervenção do navegador:

const observer = new ReportingObserver(
  (reports, observer) => {
    for (const report of reports) {
      console.log(report.type, report.url, report.body);
    }
  },
  {buffered: true}
);

observer.observe();

O callback pode ser usado para enviar relatórios a um back-end ou provedor de análise para análise posterior.

Por que isso é útil? Até agora, avisos de descontinuação e intervenção só estavam disponíveis no DevTools como mensagens do console. As intervenções, em especial, são acionadas apenas por várias restrições do mundo real, como condições do dispositivo e da rede. Portanto, talvez você nunca veja essas mensagens ao desenvolver/testar um site localmente. ReportingObserver fornece a solução para esse problema. Quando os usuários enfrentam possíveis problemas, podemos ser notificados sobre eles.

Introdução

Algum tempo atrás, escrevi uma postagem do blog ("Como observar seu app da Web") porque achei fascinante a quantidade de APIs que existem para monitorar o "conteúdo" que acontece em um app da Web. Por exemplo, há APIs que podem observar informações sobre o DOM: ResizeObserver, IntersectionObserver, MutationObserver. Existem APIs para capturar medições de performance: PerformanceObserver. Outras APIs, como window.onerror e window.onunhandledrejection, também nos avisam quando algo dá errado.

No entanto, há outros tipos de avisos que não são capturados por essas APIs existentes. Quando seu site usa uma API descontinuada ou é executado com uma intervenção do navegador, o DevTools é o primeiro a informar sobre isso:

Avisos do console do DevTools para descontinuações e intervenções.
Avisos iniciados pelo navegador no console do DevTools
.

É natural que o window.onerror capture esses avisos. Não é bem assim. Isso ocorre porque window.onerror não é acionado para avisos gerados diretamente pelo próprio user agent. Ele é disparado em erros de tempo de execução (exceções de JS e erros de sintaxe) causados pela execução do código.

ReportingObserver aproveita a folga. Ela oferece uma maneira programática de receber notificações sobre avisos emitidos pelo navegador, como suspensões de uso e intervenções. É possível usá-lo como uma ferramenta de relatórios e perder menos sono se perguntando se os usuários estão tendo problemas inesperados no seu site ativo.

A API

A API não é diferente das outras APIs de "observador", como IntersectionObserver e ResizeObserver. Você retorna um callback, que fornece informações. As informações que o callback recebe são uma lista de problemas que a página causou:

const observer = new ReportingObserver((reports, observer) => {
  for (const report of reports) {
    // → report.type === 'deprecation'
    // → report.url === 'https://reporting-observer-api-demo.glitch.me'
    // → report.body.id === 'XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload'
    // → report.body.message === 'Synchronous XMLHttpRequest is deprecated...'
    // → report.body.lineNumber === 11
    // → report.body.columnNumber === 22
    // → report.body.sourceFile === 'https://reporting-observer-api-demo.glitch.me'
    // → report.body.anticipatedRemoval === <JS_DATE_STR> or null
  }
});

observer.observe();

Relatórios filtrados

Os relatórios podem ser pré-filtrados para observar apenas determinados tipos de relatório:

const observer = new ReportingObserver((reports, observer) => {
  ...
}, {types: ['deprecation']});

Relatórios armazenados em buffer

A opção buffered: true é muito útil quando você quer ver os relatórios que foram gerados antes da criação do observador:

const observer = new ReportingObserver((reports, observer) => {
  ...
}, {types: ['intervention'], buffered: true});

Ele é ótimo para situações como carregamento lento de uma biblioteca que usa um ReportingObserver. O observador é adicionado com atraso, mas você não perde nada que aconteceu anteriormente no carregamento da página.

Parar de observar

Sim. Ele tem um método disconnect:

observer.disconnect(); // Stop the observer from collecting reports.

Exemplos

Exemplo: informe as intervenções do navegador a um provedor de análise:

const observer = new ReportingObserver(
  (reports, observer) => {
    for (const report of reports) {
      sendReportToAnalytics(JSON.stringify(report.body));
    }
  },
  {types: ['intervention'], buffered: true}
);

observer.observe();

Exemplo: receba uma notificação quando as APIs forem removidas:

const observer = new ReportingObserver((reports, observer) => {
  for (const report of reports) {
    if (report.type === 'deprecation') {
      sendToBackend(`Using a deprecated API in ${report.body.sourceFile} which will be
                     removed on ${report.body.anticipatedRemoval}. Info: ${report.body.message}`);
    }
  }
});

observer.observe();

Conclusão

O ReportingObserver oferece mais uma maneira de descobrir e monitorar possíveis problemas no seu app da Web. Ele é até uma ferramenta útil para entender a integridade da sua base de código (ou a falta dela). Envie relatórios para um back-end, saiba quais são os problemas reais que os usuários estão enfrentando no seu site, atualize o código e lucre.

Trabalho futuro

No futuro, espero que ReportingObserver se torne a API prática para capturar todos os tipos de problemas no JS. Imagine uma API para detectar tudo o que dá errado no seu app:

Também estou animado com as ferramentas que integram ReportingObserver aos fluxos de trabalho. O Lighthouse é um exemplo de ferramenta que já sinaliza descontinuações de navegadores quando você executa a auditoria "Evitar APIs descontinuadas":

A auditoria do Lighthouse para usar APIs descontinuadas pode usar o ReportingObserver.
A auditoria do Lighthouse para usar APIs descontinuadas pode usar o ReportingObserver.

Atualmente, o Lighthouse usa o protocolo DevTools para coletar as mensagens do console e informar esses problemas aos desenvolvedores. Em vez disso, pode ser interessante mudar para ReportingObserver para ver os relatórios de descontinuação bem estruturados e outros metadados, como a data anticipatedRemoval.

Outros recursos: