Prometheus: métricas customizadas na aplicação
Métricas de infraestrutura (CPU, RAM) mostram o sintoma, não a causa. Métricas de negócio — pedidos por segundo, latência de checkout, fila de emails — mostram o que realmente importa. Instrumentar a aplicação com Prometheus expõe esses indicadores para o Grafana e Alertmanager.
Instrumentar Node.js com prom-client
Adicionar endpoint /metrics ao Express:
typescript
// npm install prom-client
import express from 'express'
import { register, collectDefaultMetrics, Counter, Histogram, Gauge } from 'prom-client'
// Coletar métricas padrão do processo Node.js:
collectDefaultMetrics({ prefix: 'api_' })
// ── Definir métricas customizadas ────────────────────────
// Counter: só aumenta (requisições, erros, eventos)
const httpRequests = new Counter({
name: 'api_http_requests_total',
help: 'Total de requisições HTTP',
labelNames: ['method', 'route', 'status'],
})
// Histogram: distribuição de valores (latência, tamanho de payload)
const httpDuration = new Histogram({
name: 'api_http_duration_seconds',
help: 'Latência das requisições HTTP',
labelNames: ['method', 'route', 'status'],
buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2, 5],
})
// Gauge: pode subir e descer (conexões ativas, fila de jobs)
const activeConnections = new Gauge({
name: 'api_active_connections',
help: 'Conexões ativas no momento',
})
// Middleware para instrumentar automaticamente todas as rotas:
app.use((req, res, next) => {
const end = httpDuration.startTimer()
res.on('finish', () => {
const labels = { method: req.method, route: req.route?.path ?? req.path, status: res.statusCode }
httpRequests.inc(labels)
end(labels)
})
next()
})
// Expor endpoint para o Prometheus fazer scrape:
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType)
res.send(await register.metrics())
})Métricas de negócio: pedidos e receita
Instrumentar fluxos críticos da aplicação:
typescript
// Métricas de negócio customizadas
import { Counter, Histogram, Gauge } from 'prom-client'
const pedidosCriados = new Counter({
name: 'negocio_pedidos_criados_total',
help: 'Total de pedidos criados',
labelNames: ['plano', 'origem'],
})
const receita = new Counter({
name: 'negocio_receita_total_brl',
help: 'Receita total em BRL',
labelNames: ['plano'],
})
const filaPendente = new Gauge({
name: 'negocio_fila_pedidos_pendentes',
help: 'Pedidos aguardando processamento',
})
const tempoPagamento = new Histogram({
name: 'negocio_pagamento_duracao_segundos',
help: 'Tempo para processar pagamento',
buckets: [0.5, 1, 2, 5, 10, 30],
})
// Usar na aplicação:
async function criarPedido(dados: PedidoInput) {
const timer = tempoPagamento.startTimer()
try {
const pedido = await db.pedido.create(dados)
pedidosCriados.inc({ plano: dados.plano, origem: dados.origem })
receita.inc({ plano: dados.plano }, dados.valor)
filaPendente.inc()
timer({ status: 'success' })
return pedido
} catch (err) {
timer({ status: 'error' })
throw err
}
}Configurar scrape no Prometheus
Adicionar a aplicação como target no prometheus.yml:
yaml
# prometheus.yml — adicionar job para a aplicação:
scrape_configs:
# Infraestrutura (já existente):
- job_name: 'node_exporter'
static_configs:
- targets: ['localhost:9100']
# Aplicação Node.js:
- job_name: 'minha-api'
static_configs:
- targets: ['localhost:3000']
metrics_path: '/metrics'
scrape_interval: 15s
# Múltiplas instâncias (PM2 cluster com portas diferentes):
- job_name: 'api-cluster'
static_configs:
- targets:
- 'localhost:3000'
- 'localhost:3001'
- 'localhost:3002'
- 'localhost:3003'
relabel_configs:
- source_labels: [__address__]
target_label: instance
# Recarregar Prometheus sem restart:
curl -X POST http://localhost:9090/-/reloadQueries PromQL úteis para dashboards
Consultas para visualizar as métricas da aplicação:
bash
# Taxa de requisições por segundo (últimos 5 min):
rate(api_http_requests_total[5m])
# Latência p95 por rota:
histogram_quantile(0.95,
sum by (route, le) (
rate(api_http_duration_seconds_bucket[5m])
)
)
# Taxa de erros (5xx) por minuto:
sum(rate(api_http_requests_total{status=~"5.."}[1m]))
# Porcentagem de erros em relação ao total:
100 * sum(rate(api_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(api_http_requests_total[5m]))
# Pedidos criados por hora:
increase(negocio_pedidos_criados_total[1h])
# Receita das últimas 24h:
increase(negocio_receita_total_brl[24h])
# Fila de pedidos pendentes (instantâneo):
negocio_fila_pedidos_pendentes
# Alertas PromQL — configurar no Alertmanager:
# Alerta: taxa de erros > 5% por 5 min
ALERT HighErrorRate
IF rate(api_http_requests_total{status=~"5.."}[5m]) / rate(api_http_requests_total[5m]) > 0.05
FOR 5m
LABELS { severity="warning" }Instrumentar Python com prometheus_client
Métricas Prometheus em APIs FastAPI/Flask:
python
# pip install prometheus-client prometheus-fastapi-instrumentator
from fastapi import FastAPI
from prometheus_client import Counter, Histogram, Gauge, generate_latest, CONTENT_TYPE_LATEST
from prometheus_fastapi_instrumentator import Instrumentator
from fastapi.responses import Response
import time
app = FastAPI()
# Instrumentação automática (latência, status codes):
Instrumentator().instrument(app).expose(app)
# Métricas customizadas:
pedidos_counter = Counter(
'pedidos_criados_total',
'Total de pedidos criados',
['plano', 'status']
)
processamento_hist = Histogram(
'processamento_duracao_segundos',
'Duração do processamento de pedidos',
buckets=[0.1, 0.5, 1.0, 2.5, 5.0, 10.0]
)
@app.post("/pedidos")
async def criar_pedido(dados: PedidoInput):
inicio = time.time()
try:
pedido = await processar_pedido(dados)
pedidos_counter.labels(plano=dados.plano, status='success').inc()
return pedido
except Exception as e:
pedidos_counter.labels(plano=dados.plano, status='error').inc()
raise
finally:
processamento_hist.observe(time.time() - inicio)$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS para APIs em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.