Voltar para Artigos
DevOps10 min de leitura

Vigiando sua API: Rastreamento de Erros com Sentry

Sentry no Node.js com Express: configuração com tracesSampleRate, identificação do usuário no contexto, filtragem de erros de negócio, source maps para stack traces legíveis, alertas e performance monitoring.

12 de agosto de 2026

Um erro 500 aconteceu em produção. O usuário viu uma tela branca e fechou o app. Ele não abriu chamado de suporte — apenas foi embora. Você só vai descobrir quando alguém reclamar, ou quando monitorar os logs manualmente às 9h da manhã. Essa janela de tempo entre o erro acontecer e o time saber é onde usuários são perdidos.

O Sentry captura erros não tratados em tempo real, enriquece cada evento com contexto (usuário, request, stack trace com código fonte), agrupa erros similares para evitar ruído, e notifica imediatamente via Slack/PagerDuty. O objetivo: descobrir o bug antes do usuário abrir um chamado.

bash
npm install @sentry/node @sentry/profiling-node

Configuração no Entry Point

O Sentry deve ser inicializado como o primeiro import da aplicação, antes de qualquer outro módulo. Isso garante que erros que ocorrem durante o bootstrap (ex: falha na conexão com o banco ao inicializar) sejam capturados. O release vinculado à versão do package.json ou ao hash do commit permite correlacionar qual deploy introduziu um bug — visível no dashboard de Releases do Sentry.

server.ts (entry point — ANTES de qualquer outro import)
// Sentry DEVE ser inicializado antes de qualquer outro módulo
// para capturar erros no processo de bootstrap
import * as Sentry from '@sentry/node';
import { nodeProfilingIntegration } from '@sentry/profiling-node';

Sentry.init({
  // DSN: chave pública do seu projeto no Sentry (pode ficar no .env)
  dsn: process.env.SENTRY_DSN,

  // Ambiente: 'production', 'staging', 'development'
  environment: process.env.NODE_ENV,

  // Versão do release: usada para correlacionar erros com deploys
  // Passe a versão do package.json ou o hash do commit
  release: process.env.npm_package_version,

  // tracesSampleRate: % de transações capturadas para Performance Monitoring
  // 1.0 = 100% (caro em produção com muito tráfego)
  // 0.1 = 10% (recomendado para produção)
  tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,

  // profilesSampleRate: % das transações com profiling de CPU
  profilesSampleRate: 0.1,

  integrations: [
    nodeProfilingIntegration(),
  ],
});

// Importa o resto da aplicação DEPOIS da inicialização do Sentry
import express from 'express';
import { router } from './routes';
import { errorHandler } from './middlewares/errorHandler';

const app = express();
app.use(express.json());

Middleware de Erros Integrado

A separação entre AppError (erros de negócio esperados) e erros inesperados é fundamental para manter o Sentry útil. Se você enviar todos os erros, incluindo 'Email já cadastrado' e 'Senha incorreta', o feed do Sentry vira ruído. A meta é: cada alerta no Sentry deve ser algo que exige ação do time de engenharia. A sanitização do req.body antes de enviar ao Sentry é obrigatória — campos como password, token e cvv nunca devem aparecer nos logs.

errorHandler.ts
import type { Request, Response, NextFunction } from 'express';
import * as Sentry from '@sentry/node';
import { AppError } from '../errors/AppError';

// Middleware de erro global do Express
// DEVE ter 4 parâmetros para ser reconhecido como error handler
export function errorHandler(
  err: unknown,
  req: Request,
  res: Response,
  _next: NextFunction
): void {
  // Erros de negócio (esperados) — não enviar ao Sentry
  // São erros controlados: email duplicado, senha errada, recurso não encontrado
  if (err instanceof AppError) {
    res.status(err.statusCode).json({
      error: err.message,
      code: err.code,
    });
    return;
  }

  // Erros inesperados (bugs) — capturar no Sentry com contexto máximo
  if (err instanceof Error) {
    Sentry.withScope((scope) => {
      // Adiciona contexto do usuário autenticado (se houver)
      if (req.user) {
        scope.setUser({
          id: req.user.id,
          email: req.user.email,
        });
      }

      // Adiciona contexto da requisição
      scope.setContext('request', {
        method: req.method,
        path: req.path,
        query: req.query,
        // NÃO inclua req.body diretamente — pode conter senhas, tokens
        // Sanitize antes:
        body: sanitizeBody(req.body),
      });

      scope.setTag('route', req.route?.path ?? req.path);

      Sentry.captureException(err);
    });
  }

  // Resposta genérica para o cliente — nunca exponha detalhes do erro interno
  res.status(500).json({
    error: 'Erro interno. Nossa equipe foi notificada.',
  });
}

function sanitizeBody(body: unknown): unknown {
  if (!body || typeof body !== 'object') return body;
  const SENSITIVE_KEYS = new Set(['password', 'token', 'secret', 'cvv', 'cardNumber']);
  return Object.fromEntries(
    Object.entries(body as Record<string, unknown>).map(([key, value]) => [
      key,
      SENSITIVE_KEYS.has(key.toLowerCase()) ? '[REDACTED]' : value,
    ])
  );
}

Enriquecendo Erros com Contexto Manual

Captura manual em pontos críticos
import * as Sentry from '@sentry/node';

// Em Use Cases ou Services críticos:
export class ProcessPaymentUseCase {
  async execute(dto: ProcessPaymentDTO): Promise<Payment> {
    try {
      const payment = await this.paymentGateway.charge(dto);
      return payment;
    } catch (err) {
      // Envia ao Sentry com contexto específico do pagamento
      Sentry.captureException(err, {
        tags: {
          gateway: 'stripe',
          operation: 'charge',
        },
        extra: {
          orderId: dto.orderId,
          amount: dto.amount,
          currency: dto.currency,
          // NÃO inclua dados de cartão!
        },
        level: 'fatal', // Erro de pagamento = fatal
      });

      throw new AppError('Falha no processamento do pagamento. Tente novamente.');
    }
  }
}

// Mensagem manual (não-erro) para tracking de eventos importantes:
Sentry.captureMessage('Taxa de rejeição de pagamentos acima do normal', {
  level: 'warning',
  extra: { rejectionRate: '15%', threshold: '5%' },
});

Source Maps: Stack Traces em TypeScript

Por padrão, os stack traces mostram o código JavaScript compilado (dist/server.js:234:12), não o TypeScript original. Configure o upload de source maps para ver o arquivo e linha corretos:

bash
npm install --save-dev @sentry/cli
package.json
{
  "scripts": {
    "build": "tsc",
    "sentry:sourcemaps": "sentry-cli sourcemaps inject ./dist && sentry-cli sourcemaps upload --org sua-org --project seu-projeto ./dist",
    "deploy": "npm run build && npm run sentry:sourcemaps && pm2 reload api"
  }
}

Alertas e Integração com Slack

  1. No dashboard do Sentry, vá em Settings → Integrations → Slack
  2. Configure a integração com o workspace do Slack
  3. Em Alerts → Create Alert Rule: selecione 'A new issue is created' e 'An issue escalates' como triggers
  4. Configure o canal do Slack (ex: #alerts-producao)
  5. Configure Issue Grouping para evitar spam: agrupe por fingerprint para que o mesmo bug não gere múltiplos alertas

Configure inbound filters no Sentry para ignorar erros que você não pode controlar: erros de bots/crawlers, erros de extensões de browser, e AppError que você já trata no handler. Isso reduz o ruído e mantém o feed de alertas com apenas os erros que realmente precisam de atenção.

Conclusão

Sentry muda sua relação com erros de produção: de reativo (usuário reclama → você investiga) para proativo (Sentry alerta → você corrige antes que mais usuários sejam afetados). A configuração correta de contexto de usuário, sanitização de dados sensíveis, filtro de AppErrors e source maps transforma cada alerta em um relatório acionável com tudo o que você precisa para reproduzir e corrigir o bug.