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/-/reload

    Queries 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.

    Perguntas frequentes