Sentry: Triage y Resolución de Errores
Dominá Sentry para el seguimiento de errores en producción. Cubre integración del SDK en Python, Node.js y Java, release tracking, source maps, performance monitoring y alertas.
Introducción
Sentry captura excepciones, crashes y problemas de performance en producción. Cuando ocurre un error, recolecta el stack trace, el contexto del request, la información del usuario y los breadcrumbs que lo precedieron. Esta guía cubre la integración del SDK en Python, Node.js y Java, el seguimiento de releases con source maps, el monitoreo de performance, reglas de alerta y un flujo de trabajo práctico para triagear y resolver issues.
Si ya conocés Sentry, podés ir directo a seguimiento de releases y source maps o a reglas de alerta.
Cómo Funciona Sentry
- Un error ocurre en tu aplicación.
- El SDK de Sentry captura la excepción junto con el stack trace, el contexto del request, el usuario, breadcrumbs y tags de entorno y release.
- El SDK envía el evento al servidor de Sentry.
- Sentry deduplica y agrupa errores similares en issues.
- Sentry notifica a tu equipo por Slack, email o PagerDuty.
- Un developer triagea el issue, identifica la causa raíz y deployea un fix.
- Sentry marca el issue como resuelto cuando el nuevo release deja de reportar el error.
Los source maps convierten stack traces minificados en nombres de archivo y números de línea legibles. Sin ellos, debuggear errores de frontend o Node.js empaquetado se vuelve un juego de adivinanzas.
Integración del SDK
Python: Flask/Django
# sentry_config.py — Sentry SDK para Python
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.celery import CeleryIntegration
def init_sentry():
sentry_sdk.init(
dsn="https://your-dsn@sentry.io/123",
environment="production",
release="order-service@1.2.3",
# Sampleá 100% de errors, 10% de transactions para performance
traces_sample_rate=0.1,
sample_rate=1.0,
integrations=[
FlaskIntegration(),
SqlalchemyIntegration(),
RedisIntegration(),
CeleryIntegration(),
],
# Mandá PII (email, IP) — habilitá con caution
send_default_pii=True,
# Before send hook para filtrar sensitive data
before_send=filter_sensitive_data,
)
def filter_sensitive_data(event, hint):
"""Remové sensitive data antes de mandar a Sentry."""
if "request" in event:
headers = event["request"].get("headers", {})
# Remové authorization headers
for key in list(headers.keys()):
if key.lower() in ("authorization", "cookie", "x-api-key"):
headers[key] = "[REDACTED]"
# Remové sensitive body fields
body = event["request"].get("data", {})
if isinstance(body, dict):
for key in ("password", "credit_card", "ssn"):
if key in body:
body[key] = "[REDACTED]"
return event
# Manual error capture
from sentry_sdk import capture_exception, capture_message, set_user, add_breadcrumb
class OrderService:
def create_order(self, user_id: int, items: list[dict]) -> dict:
# Seteá user context
set_user({"id": str(user_id), "email": "user@example.com"})
# Agregá breadcrumbs para debugging
add_breadcrumb(
category="order",
message=f"Creating order for user {user_id} with {len(items)} items",
level="info",
)
try:
order = self._process_order(user_id, items)
add_breadcrumb(category="order", message="Order created successfully", level="info")
return order
except PaymentError as e:
# Captureá con extra context
capture_exception(e, {
"extra": {
"user_id": user_id,
"items_count": len(items),
"total_amount": sum(i["price"] * i["quantity"] for i in items),
},
"tags": {"error_type": "payment", "severity": "high"},
})
raise
except Exception as e:
capture_exception(e)
raise
Node.js: Express
// sentry.ts — Sentry SDK para Node.js
import * as Sentry from "@sentry/node";
import { nodeProfilingIntegration } from "@sentry/profiling-node";
export function initSentry() {
Sentry.init({
dsn: "https://your-dsn@sentry.io/123",
environment: process.env.NODE_ENV || "development",
release: `api-gateway@${process.env.APP_VERSION || "1.0.0"}`,
tracesSampleRate: 0.1,
profilesSampleRate: 0.1,
integrations: [
nodeProfilingIntegration(),
Sentry.httpIntegration(),
Sentry.expressIntegration(),
],
beforeSend(event) {
// Redactéa sensitive headers
if (event.request?.headers) {
delete event.request.headers.authorization;
delete event.request.headers.cookie;
}
// Redactéa sensitive body fields
if (event.request?.data && typeof event.request.data === "object") {
delete event.request.data.password;
delete event.request.data.creditCard;
}
return event;
},
});
}
// Express middleware — debe ir antes de routes
import express from "express";
const app = express();
Sentry.setupExpressErrorHandler(app);
// Manual error capture
import { captureException, setUser, addBreadcrumb } from "@sentry/node";
class OrderService {
async createOrder(userId: string, items: OrderItem[]): Promise<Order> {
Sentry.setUser({ id: userId, email: "user@example.com" });
Sentry.addBreadcrumb({
category: "order",
message: `Creating order for user ${userId} with ${items.length} items`,
level: "info",
});
try {
const order = await this.processOrder(userId, items);
Sentry.addBreadcrumb({
category: "order",
message: "Order created successfully",
level: "info",
});
return order;
} catch (error) {
Sentry.captureException(error, {
extra: {
userId,
itemCount: items.length,
totalAmount: items.reduce((sum, i) => sum + i.price * i.quantity, 0),
},
tags: { errorType: "order_creation", severity: "high" },
});
throw error;
}
}
}
// Custom error handler
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
Sentry.captureException(err);
res.status(500).json({ error: "Internal server error" });
});
Java: Spring Boot
// SentryConfig.java — Sentry SDK para Spring Boot
import io.sentry.Sentry;
import io.sentry.spring.tracing.SentryTracingConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SentryConfig {
@Bean
public Sentry.OptionsConfiguration sentryOptions() {
return options -> {
options.setDsn("https://your-dsn@sentry.io/123");
options.setEnvironment("production");
options.setRelease("order-service@1.2.3");
options.setTracesSampleRate(0.1);
options.setBeforeSend((event, hint) -> {
// Redactéa sensitive data
if (event.getRequest() != null) {
event.getRequest().getHeaders().remove("Authorization");
event.getRequest().getHeaders().remove("Cookie");
}
return event;
});
};
}
}
// Service code con manual capture
@Service
public class OrderService {
public Order createOrder(Long userId, List<OrderItem> items) {
Sentry.setUser(new Sentry.User(
String.valueOf(userId),
"user@example.com",
null,
null
));
Sentry.addBreadcrumb("Creating order for user " + userId, "order");
try {
Order order = processOrder(userId, items);
Sentry.addBreadcrumb("Order created successfully", "order");
return order;
} catch (PaymentException e) {
Sentry.captureException(e, Scope -> {
Scope.setExtra("user_id", userId);
Scope.setExtra("items_count", items.size());
Scope.setTag("error_type", "payment");
});
throw e;
}
}
}
Release Tracking y Source Maps
Release tracking
# Creá un Sentry release
sentry-cli releases new "order-service@1.2.3"
# Asociá commits con el release
sentry-cli releases set-commits "order-service@1.2.3" \
--auto
# Finalizá el release
sentry-cli releases finalize "order-service@1.2.3"
# Markéa deployment
sentry-cli releases deploys "order-service@1.2.3" new \
--env production \
--url "https://order-service.example.com"
Source maps para JavaScript
// webpack.config.js — Subí source maps a Sentry
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = {
devtool: "source-map",
plugins: [
new SentryWebpackPlugin({
org: "your-org",
project: "api-gateway",
authToken: process.env.SENTRY_AUTH_TOKEN,
release: "api-gateway@1.2.3",
include: "./dist",
ignore: ["node_modules", "webpack.config.js"],
// Stripéa server-side code paths
urlPrefix: "~/static/js",
}),
],
};
CI/CD integration
# .github/workflows/deploy.yml — Sentry release en CI
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Create Sentry release
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
npm run build
npx sentry-cli releases new "api-gateway@${{ github.sha }}"
npx sentry-cli releases set-commits "api-gateway@${{ github.sha }}" --auto
npx sentry-cli releases files "api-gateway@${{ github.sha }}" upload-sourcemaps ./dist
npx sentry-cli releases finalize "api-gateway@${{ github.sha }}"
- name: Deploy
run: npm run deploy
- name: Mark Sentry deploy
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: |
npx sentry-cli releases deploys "api-gateway@${{ github.sha }}" new \
--env production
Monitoreo de Performance
# Python: Custom transactions y spans
import sentry_sdk
@sentry_sdk.trace
def process_order(user_id: int, items: list[dict]) -> dict:
with sentry_sdk.start_span(op="db", description="query_user") as span:
user = db.query(User).get(user_id)
span.set_data("user.id", user_id)
with sentry_sdk.start_span(op="http.client", description="charge_payment") as span:
payment = payment_client.charge(user_id, total)
span.set_data("payment.amount", payment["amount"])
with sentry_sdk.start_span(op="db", description="save_order") as span:
order = order_repo.create(user_id, items, payment["id"])
span.set_data("order.id", order["id"])
return order
// Node.js: Custom spans
import * as Sentry from "@sentry/node";
async function processOrder(userId: string, items: OrderItem[]): Promise<Order> {
return Sentry.startSpan({ op: "function", name: "process_order" }, async () => {
const user = await Sentry.startSpan(
{ op: "db", name: "query_user" },
async () => await userRepo.findById(userId)
);
const payment = await Sentry.startSpan(
{ op: "http.client", name: "charge_payment" },
async () => await paymentService.charge(userId, total)
);
const order = await Sentry.startSpan(
{ op: "db", name: "save_order" },
async () => await orderRepo.create({ userId, items, paymentId: payment.id })
);
return order;
});
}
Reglas de Alerta
# Sentry alert rules (configurado en Sentry UI o via API)
# Example: Alertéa en high error rate
rules:
- name: "High error rate"
conditions:
- event_level: error
- threshold: 10 # errors en 1 hour
actions:
- notify_slack: "#alerts"
- notify_email: ["oncall@company.com"]
- name: "New error in production"
conditions:
- event_level: error
- environment: production
- is_new: true
actions:
- notify_slack: "#engineering"
- create_issue: true
- name: "Performance regression"
conditions:
- metric: p95_duration
- threshold: 2000 # ms
- comparison: greater_than
actions:
- notify_slack: "#performance"
Flujo de Triage
- Recibí una alerta por Slack, email o PagerDuty.
- Abrí el issue en Sentry.
- Revisá los datos clave:
- Stack trace para identificar el código que falla.
- Breadcrumbs para entender qué llevó al error.
- Contexto de usuario para saber quién se vio afectado.
- Contexto del request para ver el input que lo disparó.
- Release tag para detectar qué versión introdujo el bug.
- Tags para filtrar por entorno, servicio y tipo de error.
- Asigná el issue a un developer.
- Vinculalo a un ticket de Jira o GitHub.
- Escribí el fix y hacé el deploy.
- Sentry detecta el fix en el nuevo release y cierra el issue si no llegan eventos en 72 horas.
Buenas Prácticas
Seteá el tag release en cada deployment. Sentry lo usa para auto-resolver issues una vez que el fix está en producción.
Subí source maps para JavaScript y TypeScript minificados de forma que los stack traces apunten al código fuente real. Usá
before_send para redactar PII como contraseñas, tokens y números de tarjetas.
Seteá el contexto del usuario temprano en el ciclo del request para saber quién se vio afectado. Agregá breadcrumbs para
operaciones clave como consultas a base de datos, llamadas a APIs y cambios de estado. Usá tags como service,
endpoint, feature_flag y user_tier para filtrar. Si también usás el stack de Grafana para dashboards, consultá la
guía completa de Observability con Grafana.
Sampleá transacciones con criterio: 10% para servicios de alto tráfico y 100% para los de bajo tráfico. Agrupá errores similares con fingerprinting custom cuando el agrupamiento default no alcanza. Configurá alertas sobre nuevos errores para atrapar regresiones antes de que los usuarios las reporten, y filtrá por entorno para que los errores de desarrollo no lleguen al proyecto de producción.
Errores Comunes
No hacer seguimiento de releases imposibilita saber qué versión introdujo un bug. Siempre seteá release cuando
inicializás el SDK.
Sin source maps, los stack traces minificados son casi inútiles. Subí source maps como parte de tu pipeline de CI, no manualmente después del deploy.
Enviar demasiada data con sample rates altos genera ruido. Usá 10% de trace sampling para servicios de alto tráfico y subilo a 100% solo para paths críticos o de bajo volumen.
No redactar PII filtra contraseñas, tokens e información personal a Sentry. Usá el hook before_send para borrar campos
sensibles antes de que el evento salga de tu servidor.
Ignorar los breadcrumbs borra el camino hacia el error. Agregalos en operaciones importantes y mantené los mensajes lo suficientemente cortos como para escanearlos rápido.
Usar un solo proyecto de Sentry para todo mezcla errores de codebases distintos. Creá proyectos separados por servicio o equipo para que el triage sea enfocado.
Preguntas frecuentes
¿Qué es Sentry?
Sentry es una plataforma de seguimiento de errores y monitoreo de performance. Captura excepciones, crashes y problemas de performance con stack traces, breadcrumbs, contexto de usuario y seguimiento de releases.
¿En qué se diferencia Sentry del logging?
El logging registra eventos en un sistema de agregación para auditoría y debugging. Sentry captura errores y problemas de performance con contexto enriquecido, así que podés triagear más rápido. Usá ambos.
¿Qué son los breadcrumbs?
Los breadcrumbs son una traza de eventos que preceden a un error. Incluyen requests HTTP, consultas a base de datos, clics de UI y mensajes de log que ayudan a entender la secuencia de acciones que causó el issue.
¿Cómo resuelve Sentry los issues?
Sentry resuelve un issue cuando se deployea un nuevo release y no llegan eventos nuevos durante un período configurado, habitualmente 72 horas. También podés resolverlos manualmente.
¿Cuál es el modelo de precios de Sentry?
Sentry ofrece un tier gratuito, tiers pagos Team y Business, y planes Enterprise. Los precios escalan según la cantidad de eventos que enviés, y podés self-hostear la versión open-source si necesitás control total.
Recursos Relacionados
Logging Estructurado: logs JSON, correlation IDs, agregación
Dominá structured logging con JSON format, correlation IDs, log levels y aggregation. Cubre Python structlog, Node.js pino, Java SLF4J, stacks ELK y Loki.
GuideTrazas distribuidas: OpenTelemetry, Jaeger y Zipkin
Dominá distributed tracing con OpenTelemetry, Jaeger y Zipkin. Trace propagation entre servicios, span context, sampling strategies y debugging en producción.
RecipeMonitoreo de Usuarios Reales (RUM)
Monitorea experiencias reales de usuarios con Core Web Vitals, session replay y análisis de performance para identificar cuellos de botella del mundo real.
GuideMonitoring y Alerting
Construir un production monitoring stack. Cubre Prometheus, Grafana, AlertManager, metrics instrumentation, alert rules, runbooks, SLI/SLO/SLA, distributed tracing con Jaeger, log aggregation y on-call best practices con ejemplos practicos de configuracion.
GuideObservabilidad con el Grafana Stack
Configura metrics, logs y traces con Grafana, Prometheus, Loki y Tempo. Cubre instrumentación, dashboards, alerting y distributed tracing para sistemas en producción.
GuidePrometheus y Grafana: Metrics, Dashboards, Alerting
Dominá Prometheus metrics collection y Grafana dashboards. Cubre metric types, PromQL, service instrumentation, alerting rules y deployment en producción.