Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

PerformanceObserver no Node.js

Atualizado em: 15 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

O PerformanceObserver no Node.js permite receber entradas de desempenho conforme elas são registradas pelo runtime ou pela aplicação. Em vez de consultar listas manualmente, o observador acompanha tipos específicos, como marks, measures, funções temporizadas, garbage collection, recursos HTTP e event loop, conforme o suporte da versão.

A API faz parte do módulo node:perf_hooks e segue conceitos da Performance Timeline da Web. Ela é útil para instrumentação, profiling leve, métricas internas e diagnósticos, mas precisa de filtros e limites para não adicionar overhead ou manter entradas demais em memória.

Neste guia, você aprenderá a criar observadores, selecionar entry types, trabalhar com marks e measures, funções, GC, buffers, limpeza, histogramas, OpenTelemetry, testes e boas práticas de produção.

O que é PerformanceObserver?

PerformanceObserver recebe lotes de PerformanceEntry gerados pelo sistema de performance. A documentação oficial de PerformanceObserver no Node.js descreve a API e os tipos suportados. A documentação de PerformanceObserver na MDN explica o modelo padronizado.

Para medições básicas, consulte Performance Hooks no Node.js. Para detalhes do runtime, veja Trace Events no Node.js. O artigo Event Loop no Node.js ajuda a interpretar atrasos.

Importando a API

const {
  PerformanceObserver,
  performance
} = require('node:perf_hooks');

Observador básico

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.duration);
  }
});

observer.observe({
  entryTypes: ['measure']
});

O callback recebe uma lista com as novas entradas acumuladas para os tipos observados.

Marks e measures

performance.mark('load-start');
await loadConfiguration();
performance.mark('load-end');

performance.measure(
  'configuration-load',
  'load-start',
  'load-end'
);

O observador configurado para measure recebe a duração.

Detalhes da entrada

observer.observe({
  entryTypes: ['mark', 'measure']
});

Propriedades comuns incluem:

  • name;
  • entryType;
  • startTime;
  • duration;
  • detail, quando disponível.

Detail

performance.mark('query-start', {
  detail: {
    operation: 'list-products'
  }
});

Não inclua dados pessoais, SQL completo ou tokens no detail. O objeto pode ser clonado e mantido na timeline.

getEntriesByType()

const measures = list.getEntriesByType('measure');

Filtrar dentro do lote evita processar entradas irrelevantes quando o observador acompanha vários tipos.

getEntriesByName()

const entries = list.getEntriesByName(
  'configuration-load',
  'measure'
);

Tipos suportados

console.log(
  PerformanceObserver.supportedEntryTypes
);

Use essa lista em vez de presumir que todas as versões oferecem os mesmos tipos.

Buffered

Algumas entradas podem ser observadas com a opção buffered:

observer.observe({
  type: 'measure',
  buffered: true
});

Isso pode incluir entradas criadas antes do observador. A combinação de type e entryTypes segue regras específicas; consulte a documentação.

Observando um único tipo

observer.observe({
  type: 'function'
});

Usar type simplifica observação de um tipo e pode habilitar opções adicionais.

Temporizando funções

const timed = performance.timerify(
  async function processOrder(order) {
    return saveOrder(order);
  }
);

await timed(order);

Um observador de entradas function recebe a duração da chamada.

Argumentos de função

Entradas de timerify podem incluir detalhes ou argumentos conforme a versão. Evite instrumentar funções que recebem segredos se os dados puderem aparecer na telemetria.

Histogramas

Versões modernas permitem associar um histograma a timerify():

const {
  createHistogram
} = require('node:perf_hooks');

const histogram = createHistogram();

const timed = performance.timerify(task, {
  histogram
});

Histogramas são melhores que armazenar cada duração individual quando o volume é alto.

Garbage collection

const gcObserver = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    recordGcDuration(entry.duration);
  }
});

gcObserver.observe({
  entryTypes: ['gc']
});

Os detalhes e constantes de tipo de GC dependem da versão. Use para tendências, não para concluir sozinho que existe vazamento.

GC e memória

Correlacione pausas com:

  • RSS;
  • heap usado;
  • taxa de alocação;
  • latência;
  • event loop delay;
  • carga da aplicação.

Veja Módulo V8 no Node.js.

Entradas HTTP

O runtime pode oferecer entradas ligadas a cliente e servidor HTTP. A estrutura e estabilidade variam. Verifique supportedEntryTypes e teste os campos.

DNS e Net

Algumas versões expõem entradas para DNS e rede. Elas ajudam a separar tempo de resolução, conexão e resposta.

Consulte DNS no Node.js e TCP com Net no Node.js.

Callback do observador

O callback deve ser rápido. Não faça upload síncrono, compressão pesada ou escrita bloqueante dentro dele.

const observer = new PerformanceObserver(list => {
  const summaries = list.getEntries().map(entry => ({
    name: entry.name,
    type: entry.entryType,
    duration: entry.duration
  }));

  metricsQueue.enqueue(summaries);
});

Fila limitada

A fila de exportação precisa de limite. Se o backend de métricas estiver indisponível, descarte amostras de baixa prioridade em vez de consumir toda a memória.

Amostragem

if (Math.random() < 0.01) {
  performance.measure(...);
}

Amostragem reduz volume, mas use um gerador e estratégia consistentes quando a precisão estatística for importante.

Limpeza de marks

performance.clearMarks('load-start');
performance.clearMarks('load-end');
performance.clearMeasures('configuration-load');

Limpar evita crescimento da timeline quando as entradas não são mais necessárias.

disconnect()

observer.disconnect();

Desconecte quando a instrumentação for temporária ou durante shutdown.

takeRecords()

const pending = observer.takeRecords();

Esse método retorna entradas pendentes ainda não entregues e limpa a fila interna correspondente.

Janela de diagnóstico

function observeFor(duration) {
  const observer = createDiagnosticObserver();

  setTimeout(() => {
    const remaining = observer.takeRecords();
    processEntries(remaining);
    observer.disconnect();
  }, duration).unref();
}

Uma janela curta reduz overhead em produção.

Event loop utilization

PerformanceObserver não substitui métricas como eventLoopUtilization() e monitorEventLoopDelay(). Use-as em conjunto para entender saturação.

Correlacionando medições

Inclua identificadores limitados:

performance.mark(`request:${requestId}:start`);

IDs únicos demais podem criar cardinalidade alta. Prefira nomes estáveis e guarde correlação em contexto separado.

AsyncLocalStorage

Use AsyncLocalStorage no Node.js para manter contexto de requisição sem gerar nomes únicos para cada mark.

OpenTelemetry

PerformanceObserver pode alimentar métricas internas, enquanto OpenTelemetry conecta traces entre serviços. Consulte OpenTelemetry no Node.js.

Não duplique instrumentação

Se uma biblioteca já mede HTTP e funções, adicionar outro observador pode gerar métricas duplicadas e overhead. Defina responsabilidades claras.

Precisão

As durações usam relógio de alta resolução, mas ainda sofrem influência de escalonamento, GC e carga do sistema. Analise distribuições, não apenas uma medição.

Cardinalidade

Nomes com URL completa, ID de usuário ou query geram séries demais. Normalize:

const metricName = 'http.client.duration';

Use atributos controlados, como método e rota normalizada.

Segurança e privacidade

Entradas podem conter nomes de função, URLs, detalhes e argumentos. Filtre antes de exportar. Não envie credenciais, corpos ou dados pessoais.

Overhead

O custo depende da quantidade de entradas, callback e exportação. Faça benchmark com observação ativada e desativada.

Observador por módulo

Muitos observadores repetidos podem processar as mesmas entradas. Prefira um componente central que distribui métricas resumidas.

Testes

Cubra:

  • mark e measure;
  • timerify síncrono;
  • timerify assíncrono;
  • tipo não suportado;
  • buffered;
  • disconnect;
  • takeRecords;
  • limpeza;
  • fila cheia;
  • dados sensíveis filtrados.

Use o Node Test Runner.

Erros comuns

  • Callback pesado: a instrumentação afeta a aplicação.
  • Não limpar entradas: a timeline cresce.
  • Cardinalidade alta: o backend de métricas fica caro.
  • Presumir tipos: versões diferentes não suportam tudo.
  • Exportar argumentos: dados sensíveis vazam.
  • Guardar todas as durações: memória aumenta.
  • Não desconectar: observadores temporários permanecem ativos.

Boas práticas

  • Consulte supportedEntryTypes.
  • Observe apenas o necessário.
  • Mantenha callback rápido.
  • Use histogramas.
  • Limite filas.
  • Aplique amostragem.
  • Limpe marks e measures.
  • Desconecte observadores temporários.
  • Normalize nomes.
  • Filtre dados sensíveis.

Conclusão

O PerformanceObserver no Node.js recebe entradas de desempenho em tempo quase real e permite instrumentar marks, measures, funções e eventos do runtime.

O recurso gera valor quando a observação é seletiva e barata. Callback rápido, histogramas, limpeza, amostragem e baixa cardinalidade evitam que a telemetria se torne um novo gargalo. Com métricas correlacionadas ao event loop, memória e traces, o observador ajuda a transformar sintomas de latência em evidências mensuráveis.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita