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.




