Observabilidade com OpenTelemetry em Node.js: Guia Prático
Introdução
A observabilidade se tornou um requisito indispensável para aplicações modernas, sobretudo em ambientes de microserviços e cloud‑native. Ela permite que equipes de desenvolvimento e operação entendam o comportamento interno de um sistema, detectem anomalias e melhorem a experiência do usuário.
Neste post vamos implementar observabilidade completa (traces, métricas e logs) em uma aplicação Node.js usando OpenTelemetry, exportando dados para Jaeger e Prometheus. Você aprenderá a configurar o SDK, instrumentar código e visualizar resultados em ferramentas populares.
1. Por que Observabilidade é essencial?
- Detecção precoce de falhas antes que impactem usuários.
- Diagnóstico rápido: correlacione métricas, logs e traces.
- Otimização de performance: identifique gargalos de latência.
- Conformidade: auditorias e compliance são facilitadas com dados estruturados.
2. Conceitos básicos do OpenTelemetry
| Conceito | Descrição |
|---|---|
| Tracer | Cria spans que representam unidades de trabalho (ex.: requisição HTTP). |
| Meter | Coleta métricas (counters, gauges, histograms). |
| Exporter | Envia dados para back‑ends como Jaeger, Zipkin ou Prometheus. |
| Instrumentation Library | Pacotes que já vêm com instrumentação pronta (ex.: @opentelemetry/instrumentation-express). |
3. Configurando o ambiente
3.1 Requisitos
- Node.js >= 14
- Docker (para Jaeger e Prometheus)
- npm ou yarn
3.2 Instalando dependências
bash
npm init -y
npm install express @opentelemetry/api @opentelemetry/sdk-node
@opentelemetry/auto-instrumentations-node @opentelemetry/exporter-jaeger
@opentelemetry/exporter-prometheus
3.3 Subindo Jaeger e Prometheus com Docker Compose
Crie um arquivo docker-compose.yml:
yaml
version: "3"
services:
jaeger:
image: jaegertracing/all-in-one:1.53
ports:
- "16686:16686" # UI
- "6831:6831/udp" # UDP for Thrift
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
E um prometheus.yml mínimo:
yaml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'nodejs'
static_configs:
- targets: ['host.docker.internal:9464']
Inicie os containers: bash docker-compose up -d
4. Instrumentando a aplicação Node.js
4.1 Estrutura do projeto
my-app/ ├─ src/ │ └─ server.js ├─ otel-setup.js ├─ package.json └─ docker-compose.yml
4.2 Configuração do OpenTelemetry (otel-setup.js)
javascript // otel-setup.js const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base'); const { JaegerExporter } = require('@opentelemetry/exporter-jaeger'); const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics'); const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express'); const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
// ---------- Tracing ---------- const tracerProvider = new NodeTracerProvider(); const jaegerExporter = new JaegerExporter({ endpoint: 'http://localhost:14268/api/traces', // Jaeger HTTP collector }); tracerProvider.addSpanProcessor(new SimpleSpanProcessor(jaegerExporter)); tracerProvider.register();
// ---------- Metrics ---------- const prometheusExporter = new PrometheusExporter({ port: 9464, endpoint: '/metrics', }); const metricReader = new PeriodicExportingMetricReader({ exporter: prometheusExporter, exportIntervalMillis: 5000, }); const meterProvider = new MeterProvider(); meterProvider.addMetricReader(metricReader); meterProvider.register();
// ---------- Auto‑instrumentation ---------- registerInstrumentations({ instrumentations: [ new HttpInstrumentation(), new ExpressInstrumentation(), ], });
console.log('OpenTelemetry configurado: Jaeger (traces) e Prometheus (metrics)');
4.3 Servidor Express (src/server.js)
javascript // src/server.js require('../otel-setup'); // Must be imported before any other module const express = require('express'); const { Counter } = require('@opentelemetry/api').metrics;
const app = express(); const port = process.env.PORT || 3000;
// Métrica customizada: contagem de requisições HTTP const meter = require('@opentelemetry/api').metrics.getMeter('my-app-meter'); const requestCounter = meter.createCounter('http_requests_total', { description: 'Contagem total de requisições HTTP', });
app.use(express.json());
app.get('/health', (req, res) => { requestCounter.add(1, { route: '/health', method: 'GET' }); res.json({ status: 'ok' }); });
app.get('/compute/:n', (req, res) => { const n = parseInt(req.params.n, 10); requestCounter.add(1, { route: '/compute/:n', method: 'GET' }); const fib = fibonacci(n); res.json({ n, fib }); });
function fibonacci(num) { if (num <= 1) return num; return fibonacci(num - 1) + fibonacci(num - 2); }
app.listen(port, () => {
console.log(Servidor rodando na porta ${port});
});
Importante:
otel-setup.jsdeve ser carregado antes de qualquer outro módulo para garantir que a instrumentação automática capture todas as chamadas.
5. Exportando e visualizando dados
5.1 Jaeger (Traces)
Acesse a UI do Jaeger em http://localhost:16686. Procure pelo serviço my-app e explore os spans gerados pelas rotas /health e /compute/:n. Você verá a hierarquia de chamadas, tempos de execução e atributos customizados.
5.2 Prometheus (Métricas)
A UI do Prometheus fica em http://localhost:9090. Use a query:
http_requests_total{route="/compute/:n"}
para observar o contador de requisições. O exporter do OpenTelemetry já expõe a métrica em http://localhost:9464/metrics.
6. Boas práticas e próximos passos
- Context Propagation: assegure que contextos de trace sejam propagados em chamadas assíncronas (promises, callbacks).
- Sampling: ajuste políticas de sampling para evitar sobrecarga em produção.
- Custom Attributes: adicione atributos relevantes (userId, tenantId) aos spans para enriquecer a análise.
- Alertas: configure alertas no Prometheus (ou Grafana) para métricas críticas como latência > 500 ms.
- Exporters adicionais: OpenTelemetry suporta Zipkin, OTLP, Elastic APM, entre outros.
Conclusão
Implementar observabilidade com OpenTelemetry em Node.js não requer esforço excessivo e traz ganhos imediatos de visibilidade. Ao combinar traces (Jaeger) e métricas (Prometheus) você obtém um panorama completo do comportamento da aplicação, facilitando diagnóstico, otimização e escalabilidade.
Próximos passos recomendados:
- Integrar logs estruturados com OpenTelemetry Logging.
- Automatizar a coleta de métricas de recursos (CPU, memória) usando o collector oficial.
- Avaliar estratégias de sampling avançado para ambientes de alta taxa de requisição.
Com esses fundamentos, sua equipe está pronta para adotar práticas de observabilidade que sustentam sistemas resilientes e de alto desempenho.



