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.
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.
npm install @sentry/node @sentry/profiling-nodeConfiguraçã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.
// 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.
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
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:
npm install --save-dev @sentry/cli{
"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
- No dashboard do Sentry, vá em Settings → Integrations → Slack
- Configure a integração com o workspace do Slack
- Em Alerts → Create Alert Rule: selecione 'A new issue is created' e 'An issue escalates' como triggers
- Configure o canal do Slack (ex:
#alerts-producao) - 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.