Error Tracking con Sentry en Express
Cómo integrar Sentry para error tracking en aplicaciones Node.js Express, incluyendo error handlers, performance monitoring, release tracking y source maps.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Overview
Sentry captura errores no manejados en tu aplicación, los agrupa por similitud y envía alerts. Muestra el stack trace, context del request, info del usuario y breadcrumbs (eventos que llevaron al error). En Express, el middleware de Sentry envuelve el pipeline de requests para que cualquier error thrown o unhandled rejection se capture automáticamente.
When to Use
- Aplicaciones en producción donde necesitas alerts de error en tiempo real
- Trackear error rates y detección de regresión a través de releases
- Capturar bugs reportados por usuarios con stack traces completos y context del request
- Monitorear performance (transaction tracing) junto con error tracking
- Triage de bugs por severidad, frecuencia y usuarios afectados
When NOT to Use
- Desarrollo local — usa el debugger y output de consola
- Aplicaciones sin error handling — arregla tus try/catch blocks primero
- Herramientas internas de bajo tráfico — el free tier de Sentry tiene límites de eventos
- Observability solo con logging — Sentry es para errores, no para shipping general de logs
Solution
Setup
npm install @sentry/node
Integración básica con Express
const express = require("express");
const Sentry = require("@sentry/node");
Sentry.init({
dsn: "https://your-dsn@sentry.io/123",
environment: process.env.NODE_ENV || "development",
tracesSampleRate: 0.1,
});
const app = express();
// El request handler de Sentry debe ser el primer middleware
app.use(Sentry.Handlers.requestHandler());
app.get("/api/users/:id", (req, res) => {
if (req.params.id === "0") {
throw new Error("Invalid user ID");
}
res.json({ id: req.params.id, name: "Alice" });
});
// El error handler de Sentry debe ir después de todas las rutas
app.use(Sentry.Handlers.errorHandler());
app.listen(3000);
Captura manual de errores
Sentry.captureException(new Error("Something went wrong"));
Sentry.captureMessage("Rate limit approaching", "warning");
try {
JSON.parse("{invalid}");
} catch (err) {
Sentry.captureException(err);
}
Agregar context a errores
// Setear user context
Sentry.setUser({
id: 42,
email: "alice@example.com",
username: "alice",
});
// Setear tags (filtrables en el UI de Sentry)
Sentry.setTag("page_locale", "en-US");
Sentry.setTag("feature_flag", "new_checkout");
// Setear extra context (searchable pero no filterable)
Sentry.setExtra("order_id", "ord-123");
Sentry.setExtra("cart_items", 3);
// Limpiar user después de logout
Sentry.setUser(null);
Breadcrumbs para debugging
Sentry.addBreadcrumb({
category: "auth",
message: "User authenticated",
level: "info",
data: { method: "oauth", provider: "google" },
});
Sentry.addBreadcrumb({
category: "http",
message: "GET /api/users/42",
level: "info",
data: { status_code: 200, duration_ms: 45 },
});
// Si un error ocurre después, Sentry muestra estos breadcrumbs
Transaction personalizada para performance monitoring
const transaction = Sentry.startTransaction({
name: "process_order",
op: "function",
});
try {
const span = transaction.startChild({ op: "validate_payment" });
// ... validar pago ...
span.finish();
const dbSpan = transaction.startChild({ op: "save_order" });
// ... guardar en DB ...
dbSpan.finish();
} catch (err) {
Sentry.captureException(err);
} finally {
transaction.finish();
}
Release tracking
Sentry.init({
dsn: "https://your-dsn@sentry.io/123",
release: process.env.SENTRY_RELEASE || "1.0.0",
environment: process.env.NODE_ENV,
});
// Sentry ahora agrupa errores por release y muestra detección de regresión
Source maps para stack traces legibles
npm install @sentry/cli --save-dev
// sentry.config.js
module.exports = {
org: "your-org",
project: "your-project",
authToken: process.env.SENTRY_AUTH_TOKEN,
release: process.env.SENTRY_RELEASE,
};
Sube source maps durante el build:
sentry-cli sourcemaps upload --release 1.0.0 ./dist
Integración con Winston
const winston = require("winston");
const Sentry = require("@sentry/node");
const logTransport = new winston.transports.Console();
const sentryTransport = {
log(info) {
if (info.level === "error") {
Sentry.captureException(new Error(info.message));
}
return true;
},
};
const logger = winston.createLogger({
transports: [logTransport, sentryTransport],
});
logger.error("Database connection failed");
// Envía tanto a console como a Sentry
Filtrado y sampling
Sentry.init({
dsn: "https://your-dsn@sentry.io/123",
tracesSampleRate: 0.1, // 10% de transactions
ignoreErrors: [
"ResizeObserver loop limit exceeded",
"Non-Error promise rejection captured",
],
denyUrls: [
/extensions\//i,
/safari-extension/i,
],
beforeSend(event) {
if (event.request.url.includes("/health")) {
return null; // No enviar errores de health check
}
return event;
},
});
Variants
Usar Sentry con Fastify
const Fastify = require("fastify");
const Sentry = require("@sentry/node");
Sentry.init({ dsn: "https://your-dsn@sentry.io/123" });
const fastify = Fastify();
fastify.addHook("onRequest", async (request, reply) => {
request.sentry = Sentry.getCurrentHub();
});
fastify.addHook("onError", async (request, reply, error) => {
Sentry.captureException(error);
});
fastify.get("/api/error", async () => {
throw new Error("Fastify error");
});
Usar Sentry con TypeScript
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
tracesSampleRate: 0.1,
});
interface UserContext {
id: string;
email: string;
}
function setSentryUser(user: UserContext): void {
Sentry.setUser(user);
}
Best Practices
-
For a deeper guide, see Sentry: Error Tracking, Triage, and Resolution.
-
Coloca
Sentry.Handlers.requestHandler()antes de todo otro middleware — necesita envolver el request completo -
Coloca
Sentry.Handlers.errorHandler()después de todas las rutas — atrapa errores que se escapan -
Setea
releasea tu git commit SHA o version tag — habilita detección de regresión -
Sube source maps en CI — los stack traces minificados son inútiles sin ellos
-
Usa
beforeSendpara filtrar ruido (health checks, 404s de assets, errores de extensiones de navegador) -
Setea
tracesSampleRatea 0.1 en producción — 1.0 abruma tu quota de Sentry -
Usa
setUser()para identificar quién experimentó el error — invaluable para tickets de soporte -
Agrega breadcrumbs para acciones clave (auth, API calls, state changes) para trazar el path del error
Common Mistakes
- No usar el middleware de error handler: sin
Sentry.Handlers.errorHandler(), el error handler default de Express atrapa errores antes que Sentry. - Setear DSN en código: usa una environment variable. Commitear el DSN lo expone.
- No subir source maps: el código minificado produce stack traces ilegibles. Siempre súbelos en CI.
- Enviar demasiados eventos: sin sampling o filtrado, Sentry envía cada error. Filtra ruido con
ignoreErrorsydenyUrls. - No limpiar user context en logout: los errores después de logout se atribuyen al usuario anterior.
FAQ
¿Cómo testeo que Sentry funciona?
Crea una ruta de test:
app.get("/debug-sentry", () => {
throw new Error("Sentry test error");
});
Visita la ruta y revisa tu dashboard de Sentry para el error.
¿Cuál es la diferencia entre captureException y captureMessage?
captureException toma un Error object e incluye el stack trace. captureMessage toma un string y crea un evento a nivel de breadcrumb. Usa captureException para errores y captureMessage para warnings o info.
¿Cómo manejo errores async?
El middleware de Sentry para Express atrapa errores async automáticamente en Express 5+. Para Express 4, envuelve los async handlers:
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
app.get("/api/async", asyncHandler(async (req, res) => {
const data = await fetchData();
res.json(data);
}));
¿Puedo usar Sentry para errores frontend también?
Sí. Instala @sentry/react o @sentry/vue para frontend. Usa la misma organización y proyecto — Sentry separa errores frontend y backend por plataforma.
¿Cómo reduzco costos de Sentry?
- Baja
tracesSampleRate(0.01 para apps de alto tráfico) - Usa
ignoreErrorspara filtrar ruido conocido - Usa
beforeSendpara dropear eventos de health checks y assets estáticos - Setea
maxBreadcrumbsa 20 en lugar del default 100
Recursos Relacionados
High-Performance Logging with pino
How to use pino for fast structured JSON logging in Node.js, including log levels, child loggers, transports, and integration with Express and Fastify.
RecipeRotate Logs Daily with Winston
How to configure daily log rotation in Node.js using winston and winston-daily-rotate-file, including size limits, retention, compression, and transport combining.
RecipeStructured JSON Logging with structlog
How to emit structured JSON logs in Python using structlog, including context binding, log levels, processors, and integration with standard logging.