Guía completa de monitorización en producción con Next.js: integración de Sentry, gestión de logs y alertas

Son las 9:17 de la noche del viernes y el móvil vibra.
El grupo de chat ya arde: «La página de pago no abre», «El pedido falló», «Pantalla en blanco». En local, todo funciona perfecto. Revisas los logs del servidor y solo aparece una línea: «Internal Server Error». El usuario dice que la página se congela al pulsar pagar, pero no logras reproducirlo.
Ese fin de semana repasaste los logs de despliegue en Vercel, redeployaste una versión de prueba y al final descubriste que el SDK de pago de terceros hacía timeout de forma intermitente en producción. Todo el proceso fue como buscar una aguja en la oscuridad.
Tu app Next.js puede ir como seda en desarrollo, pero en producción es otra historia — SSR que a veces devuelve 500, funciones edge que fallan sin motivo aparente, tiempos de respuesta de la API que se disparan sin saber dónde está el cuello de botella.
La raíz es simple: te falta un sistema completo de monitorización en producción. En este artículo montamos paso a paso un sistema para Next.js: desde Sentry hasta logs estructurados, rendimiento y alertas. Nada de teoría vacía: configuraciones que puedes copiar y pegar. Cuando leas esto, la próxima incidencia la sabrás antes que tus usuarios.
Por qué Next.js necesita un plan de monitorización específico
La triple naturaleza de Next.js
Una app frontend tradicional solo corre en el navegador; los errores los ves en DevTools. Next.js es distinto: la misma aplicación corre en tres entornos completamente diferentes:
- Cliente (Browser): componentes React en el navegador del usuario
- Servidor (Node.js): renderizado SSR, API Routes, Server Actions
- Red perimetral (Edge Runtime): middleware, funciones edge
Un flujo de pago puede pasar por: validación del formulario en cliente → autenticación en middleware → Server Action → API Route que consulta la base de datos → respuesta al cliente. Si falla cualquier eslabón, la monitorización clásica del navegador no ve el panorama completo.
El año pasado tuve un bug muy raro: un usuario reportó «la página carga muy lento y luego muestra 500». El panel Network mostraba peticiones lentas, pero no dónde. Tras integrar el rastreo distribuido de Sentry, descubrimos que en el SSR se llamaba a una API de terceros que pasó de 200 ms habituales a 8 segundos. La monitorización del cliente nunca habría detectado eso.
El efecto caja negra del SSR
Cuando falla el renderizado en servidor, el usuario suele ver solo una página 500 desnuda. Sin stack trace, sin contexto, nada.
Peor aún: los errores de hidratación. Puede que hayas visto esta advertencia:
Warning: Expected server HTML to contain a matching <div> in <div>
En desarrollo puede pasar desapercibida; en producción puede dejar toda la página inutilizable. Sin monitorización, solo te enteras cuando un usuario dice «la página no responde al clic».
Según datos de Vercel, los errores relacionados con SSR representan alrededor del 35% de los problemas en producción con Next.js. Y eso solo son errores, no rendimiento: un componente cuyo SSR se alarga de repente hace que la página «se sienta lenta» sin que sepas dónde está el cuello de botella.
Los cuatro pilares de una monitorización completa
Un plan fiable para Next.js debe cubrir:
Seguimiento de errores
No basta con capturar excepciones: hay que saber quién la disparó (usuario), en qué entorno (dispositivo, navegador, red), qué hacía (breadcrumbs) y qué parámetros tenía la petición.
Monitorización de rendimiento
¿LCP supera 2,5 s? ¿La API responde más lento? ¿Qué consulta a la base de datos frena toda la petición?
Gestión de logs
Logs estructurados, búsqueda rápida por tiempo, usuario o ID de petición. Salida legible en desarrollo; en producción, envío a una plataforma centralizada.
Configuración de alertas
Notificar al equipo cuando la tasa de error supere un umbral; avisar en Slack ante un error nuevo; alertar automáticamente ante regresiones de rendimiento.
Con estos cuatro pilares dejas de ir a ciegas. Vamos uno por uno.
Integración de Sentry en la práctica: de la instalación a la configuración avanzada
Integración rápida en 5 minutos
Sentry tiene soporte maduro para Next.js y un asistente de configuración automática. En serio, 5 minutos:
# Instalar el SDK
npm install @sentry/nextjs
# Ejecutar el asistente
npx @sentry/wizard@latest -i nextjs
El asistente pregunta por el DSN del proyecto Sentry, si subir Source Maps, etc., y crea tres archivos:
sentry.client.config.ts— entorno del navegadorsentry.server.config.ts— servidor Node.jssentry.edge.config.ts— Edge Runtime
También modifica next.config.js con el plugin webpack de Sentry. Tras el asistente, la monitorización básica ya funciona.
Pero eso es solo el inicio. En producción hace falta afinar más.
Puntos clave de captura de errores con App Router
Si usas App Router, hay varios detalles importantes:
Manejo global de errores
Crea app/global-error.tsx, la última línea de defensa en App Router:
'use client';
import * as Sentry from '@sentry/nextjs';
import { useEffect } from 'react';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Enviar a Sentry
Sentry.captureException(error);
}, [error]);
return (
<html>
<body>
<div style={{ padding: '2rem', textAlign: 'center' }}>
<h2>Algo salió mal</h2>
<p>Hemos registrado el error y lo corregiremos pronto</p>
<button onClick={() => reset()}>Reintentar</button>
</div>
</body>
</html>
);
}
Captura de errores en Server Actions
Las Server Actions son una joya de App Router, pero a menudo se olvida el manejo de errores:
'use server';
import * as Sentry from '@sentry/nextjs';
export async function createOrder(formData: FormData) {
return await Sentry.withServerActionInstrumentation(
'createOrder', // nombre visible en Sentry
{
recordResponse: true, // registrar respuesta
},
async () => {
// tu lógica de negocio
const productId = formData.get('productId');
const order = await db.order.create({
data: { productId, userId: getCurrentUserId() },
});
return order;
}
);
}
Así, cualquier error en la Server Action se reporta automáticamente y puedes medir el tiempo de ejecución.
Configuración optimizada para producción
Tras integrar Sentry, la factura del primer mes puede asustar: la configuración por defecto envía todos los eventos. Hay que ajustar el muestreo:
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Tasa de muestreo de trazas
// 100% en desarrollo, 10% en producción (ajusta según tráfico)
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Session Replay
replaysSessionSampleRate: 0.1, // 10% de sesiones normales
replaysOnErrorSampleRate: 1.0, // 100% de sesiones con error
// Identificador de entorno
environment: process.env.NEXT_PUBLIC_VERCEL_ENV || 'development',
// Ignorar errores concretos
ignoreErrors: [
// Errores de extensiones del navegador
'ResizeObserver loop limit exceeded',
// Scripts de terceros
/chrome-extension/,
/^Non-Error promise rejection/,
],
});
Guía de tracesSampleRate:
- UV diarias < 10 000: 0,2 - 0,5
- UV diarias 10 000 - 100 000: 0,1 - 0,2
- UV diarias > 100 000: 0,05 - 0,1
En nuestro proyecto (~30 000 UV/día) usamos 0,15: unos 60% de la cuota mensual de Sentry, muestra suficiente y no nos pasamos de presupuesto.
Source Maps: depurables sin filtrar código
El JavaScript en producción suele estar minificado y ofuscado; el stack parece:
at r.render (app.js:1:23456)
Ilegible. Los Source Maps mapean el código ofuscado al original, pero exponerlos filtra el código fuente.
Sentry sube los Source Maps a sus servidores: el navegador del usuario no los recibe; Sentry los usa internamente para reconstruir el stack.
Configuración en CI/CD (ejemplo con GitHub Actions):
# .github/workflows/deploy.yml
- name: Upload Source Maps to Sentry
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: your-org
SENTRY_PROJECT: your-project
run: npm run build
El plugin de Sentry en next.config.js gestiona la subida. Añade SENTRY_AUTH_TOKEN a GitHub Secrets; no lo commitees.
Funciones avanzadas: Session Replay y rastreo distribuido
Session Replay es de mis favoritas: reproduces la sesión del usuario como un vídeo.
Un usuario reportó «el botón de pago no responde». En su Session Replay vi que usaba iPad en horizontal y el teclado virtual tapaba el botón. Solo con logs de error nunca lo habríamos visto.
Para activarlo, en la config del cliente:
import * as Sentry from '@sentry/nextjs';
import { Replay } from '@sentry/nextjs';
Sentry.init({
integrations: [
new Replay({
maskAllText: false, // ocultar todo el texto
blockAllMedia: true, // bloquear medios
maskAllInputs: true, // ocultar inputs (evitar datos sensibles)
}),
],
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
});
El rastreo distribuido sigue el ciclo de vida completo de una petición: clic → petición frontend → API Route → consulta a BD → renderizado. Ves el tiempo de cada paso.
La configuración es sencilla: misma config Sentry.init en cliente y servidor; el SDK propaga sentry-trace en las cabeceras.
Contexto personalizado: errores con más sentido
Por defecto Sentry solo sabe que «ocurrió un error». Con contexto personalizado, el informe vale mucho más:
import * as Sentry from '@sentry/nextjs';
// Información del usuario
Sentry.setUser({
id: user.id,
email: user.email,
username: user.username,
// ¡No pongas contraseñas ni datos sensibles!
});
// Contexto de negocio
Sentry.setContext('purchase', {
orderId: '12345',
amount: 99.99,
paymentMethod: 'credit_card',
});
// Etiquetas (para filtrar)
Sentry.setTag('feature', 'checkout');
Sentry.setTag('ab_test', 'variant_b');
Cuando ocurre el error, sabes de inmediato qué usuario y en qué escenario de negocio.
Caso real: detectamos un error de pago más frecuente de lo esperado; en Sentry vimos que eran todos usuarios con ab_test: variant_b. El nuevo flujo de pago del A/B test tenía un bug; cerramos esa variante a tiempo.
Gestión de logs: que trabajen para ti
console.log ya no basta
Al principio también usaba console.log por todas partes. En depuración va bien; en producción te deja colgado:
- Sin filtrar: ¿buscar la petición de un usuario entre 100 000 líneas?
- Sin agregar: ¿cuántas consultas a BD tardaron más de 1 s en la última hora?
- Sin alertas: ¿«Payment failed» en los logs? Nadie se entera.
Los logs estructurados lo resuelven: no imprimes strings sueltos, sino objetos JSON con timestamp, nivel, ID de petición, ID de usuario, etc. Luego buscas y agregas por cualquier campo.
Pino vs Winston: cómo elegir
Dos grandes librerías de logs en Node.js:
| Característica | Pino | Winston |
|---|---|---|
| Rendimiento | Muy rápido, casi sin coste async | Un poco más lento, pero suficiente |
| Facilidad | Config sencilla, listo para usar | Más funciones, buen ecosistema de plugins |
| Extensibilidad | Vía Transport | Transports integrados |
| Comunidad | Recomendado en docs de Next.js | Clásico, documentación amplia |
Mi recomendación:
- Alto tráfico (QPS > 1000): Pino, ventaja clara de rendimiento
- Procesamiento complejo (múltiples formatos/destinos): Winston
- Si dudas: Pino; la documentación oficial de Next.js lo usa
Configuración práctica con Pino
Instalación:
npm install pino
npm install pino-pretty --save-dev # salida legible en desarrollo
Logger global:
// lib/logger.ts
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
// Formato del nivel
formatters: {
level: (label) => ({ level: label.toUpperCase() }),
},
// pino-pretty en desarrollo
transport: process.env.NODE_ENV === 'development'
? {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss',
ignore: 'pid,hostname',
},
}
: undefined,
});
export { logger };
En desarrollo: salida coloreada y legible. En producción: JSON para la plataforma de logs.
Uso en API Route
Lo clave: asignar un correlationId (ID de correlación) a cada petición y encadenar todos los logs:
// app/api/products/route.ts
import { logger } from '@/lib/logger';
import { randomUUID } from 'crypto';
export async function GET(request: Request) {
// ID único de la petición
const correlationId = request.headers.get('x-correlation-id') || randomUUID();
// Logger hijo con correlationId
const log = logger.child({ correlationId });
try {
log.info({ url: request.url }, 'Processing product request');
const products = await db.product.findMany();
log.info({ count: products.length }, 'Products fetched successfully');
return Response.json(products);
} catch (error) {
log.error({ error: error.message, stack: error.stack }, 'Failed to fetch products');
throw error;
}
}
Todos los logs con el mismo correlationId quedan vinculados; al depurar, buscas ese ID y ves toda la cadena.
Buenas prácticas de niveles de log
Pocos logs no sirven; demasiados enterran lo importante. Mi criterio:
ERROR — requiere acción inmediata
- Fallo de conexión a base de datos
- Fallo de API de pago
- Excepción en lógica crítica
log.error({ error, userId, orderId }, 'Payment processing failed');
WARN — anomalía pero recuperable
- Reintento de API exitoso
- Lógica de degradación activada
- Cerca del límite de cuota
log.warn({ retryCount: 3 }, 'External API retry succeeded');
INFO — hitos de negocio
- Login/logout
- Creación/cierre de pedido
- Cambios de configuración importantes
log.info({ userId, ip }, 'User logged in');
DEBUG — detalle para depuración
- Parámetros y retornos de funciones
- Estados intermedios
- Mediciones de tiempo
log.debug({ params }, 'Calling external API');
En producción, nivel INFO por defecto; sube a DEBUG solo cuando investigas un incidente.
Agregación y análisis de logs
En local, pino-pretty basta. En producción, una plataforma centralizada. Opciones habituales:
Vercel Logs
Si despliegas en Vercel, logs integrados sin configuración. Solo 7 días de retención y búsqueda limitada.
Datadog
Solución enterprise: APM + logs + monitorización. Configuración:
import { datadogLogs } from '@datadog/browser-logs';
datadogLogs.init({
clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN,
site: 'datadoghq.com',
forwardErrorsToLogs: true,
sampleRate: 100,
});
Logtail/BetterStack
Buena relación calidad-precio, centrado en análisis de logs. Búsqueda en tiempo real, alertas, paneles personalizados.
En proyectos personales uso Logtail (1 GB/mes gratis). En equipo, Datadog: caro pero potente.
Diseño de campos clave en logs
Un buen log debería incluir:
{
"timestamp": "2025-12-20T15:00:06.123Z", // timestamp
"level": "INFO", // nivel
"correlationId": "abc-123-def", // ID de correlación
"userId": "user_456", // ID de usuario
"action": "create_order", // acción de negocio
"duration": 234, // duración (ms)
"status": "success", // estado
"metadata": { // metadatos extra
"orderId": "order_789",
"amount": 99.99
}
}
Así respondes: quién hizo qué, cuándo y con qué resultado.
Monitorización de rendimiento: optimización basada en datos
Core Web Vitals: las métricas que le importan a Google
Google usa Core Web Vitals en el ranking de búsqueda; tú también deberías:
- LCP (Largest Contentful Paint): tiempo de pintado del contenido principal, ideal < 2,5 s
- FID (First Input Delay) / INP (Interaction to Next Paint): respuesta a interacción, < 100 ms / < 200 ms
- CLS (Cumulative Layout Shift): desplazamiento acumulado de layout, < 0,1
Next.js incluye reporte de Web Vitals; en app/layout.tsx añade unas líneas:
'use client';
import { useReportWebVitals } from 'next/web-vitals';
export function WebVitalsReporter() {
useReportWebVitals((metric) => {
// Enviar a Sentry
if (window.Sentry) {
window.Sentry.captureMessage(`Web Vital: ${metric.name}`, {
level: 'info',
tags: {
web_vital: metric.name,
},
contexts: {
web_vitals: {
value: metric.value,
rating: metric.rating,
},
},
});
}
// O a tu plataforma de analytics
fetch('/api/analytics/web-vitals', {
method: 'POST',
body: JSON.stringify(metric),
});
});
return null;
}
Luego impórtalo en el layout raíz:
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<body>
<WebVitalsReporter />
{children}
</body>
</html>
);
}
Seguimiento de rendimiento de API
El frontend es la mitad; si la API va lenta, el usuario también espera. Sentry Performance Monitoring rastrea cada petición:
// app/api/products/[id]/route.ts
import * as Sentry from '@sentry/nextjs';
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
// Crear un span
return await Sentry.startSpan(
{
op: 'api.request',
name: 'GET /api/products/[id]',
},
async () => {
// Rastrear consulta a BD
const product = await Sentry.startSpan(
{
op: 'db.query',
name: 'Fetch product from database',
},
async () => {
return await db.product.findUnique({
where: { id: params.id },
include: { reviews: true },
});
}
);
if (!product) {
return Response.json({ error: 'Not found' }, { status: 404 });
}
// Rastrear API externa
const pricing = await Sentry.startSpan(
{
op: 'http.client',
name: 'Fetch pricing from external API',
},
async () => {
const res = await fetch(`https://pricing-api.com/product/${params.id}`);
return res.json();
}
);
return Response.json({ ...product, pricing });
}
);
}
En el panel de Sentry verás:
- Petición total: 450 ms
- Consulta BD: 120 ms
- API externa: 300 ms
- Resto: 30 ms
De un vistazo: el cuello de botella es la API externa.
Alertas por consultas lentas
La base de datos suele ser el cuello de botella. Puedes monitorizar en middleware de Prisma:
// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
import { logger } from './logger';
const prisma = new PrismaClient();
// Monitorizar consultas lentas
prisma.$use(async (params, next) => {
const before = Date.now();
const result = await next(params);
const after = Date.now();
const duration = after - before;
// Más de 1 s → WARN
if (duration > 1000) {
logger.warn({
model: params.model,
action: params.action,
duration,
args: params.args,
}, 'Slow database query detected');
// También a Sentry
Sentry.captureMessage('Slow database query', {
level: 'warning',
tags: { model: params.model, action: params.action },
extra: { duration, args: params.args },
});
}
return result;
});
export { prisma };
Así ninguna consulta lenta se escapa.
Monitorización real de usuarios vs monitorización sintética
RUM (Real User Monitoring)
Sentry, Datadog, etc. recogen datos de usuarios reales. Refleja escenarios reales (red, dispositivo); es reactivo: te enteras cuando ya pasó.
Monitorización sintética (Synthetic Monitoring)
Checkly, Pingdom, etc. simulan visitas periódicas desde varias regiones. Proactiva; no cubre todos los escenarios de usuario.
Lo ideal: combinar ambas:
- RUM para métricas de experiencia
- Sintética para disponibilidad y flujos críticos (login, pago)
Uso Checkly cada 5 minutos desde 5 ubicaciones contra la home y el login; cualquier timeout o fallo dispara alerta.
Configuración de alertas: detectar problemas al instante
Integración con Slack: que el equipo lo sepa de inmediato
Integrar Sentry con Slack es sencillo: Settings → Integrations → Slack, autorizas y eliges el canal.
La config por defecto envía todos los errores y pronto se convierte en ruido. Hay que definir reglas de alerta:
En la configuración del proyecto Sentry:
- Alerts → Create Alert Rule
- Condiciones:
- Tasa de error: «más de 50 errores en 10 minutos»
- Error nuevo: «notificar al aparecer por primera vez»
- Regresión de rendimiento: «P95 de API > 1 s»
- Action: Send a notification via Slack
Formato del mensaje en Slack:
🚨 Production Error Spike
Project: my-nextjs-app
Environment: production
Error: TypeError: Cannot read property 'id' of undefined
Events: 127 events in 10 minutes
View in Sentry: https://sentry.io/...
Un clic y vas directo al detalle y al stack en Sentry.
Niveles de alerta: evitar la fatiga
No todo tiene la misma urgencia. Mi estrategia:
P0 — crítico (acción inmediata)
- Servicio completamente caído
- Fallo en pagos
- Pérdida de conexión a base de datos
Canal: teléfono (PagerDuty) + Slack @channel
P1 — importante (respuesta en 1 hora)
- Funcionalidad core degradada
- Pico de errores (> 100 en 10 min)
- P95 de API > 3 s
Canal: Slack en canal de desarrollo
P2 — normal (en horario laboral)
- Errores acotados (< 10/hora)
- Funciones no críticas
- Scripts de terceros
Canal: resumen diario por email
Ejemplo de reglas (Sentry Alert Rule):
// Alerta P0: fallo de pago
{
conditions: [
{ type: 'event.tag', key: 'feature', value: 'payment' },
{ type: 'event.level', value: 'error' }
],
frequency: 'every event', // notificar siempre
actions: [
{ type: 'slack', channel: '#critical-alerts', mention: '@channel' },
{ type: 'pagerduty', service: 'payments' }
]
}
// Alerta P1: pico de errores
{
conditions: [
{ type: 'event.count', value: 100, interval: '10m' }
],
frequency: 'once per issue', // una vez por issue
actions: [
{ type: 'slack', channel: '#alerts-dev' }
]
}
Técnicas para reducir ruido en alertas
Al principio las alertas pueden abrumarte. Algunos trucos:
1. Ignorar problemas conocidos
Errores de hot reload en desarrollo, scripts de terceros: filtrarlos:
// sentry.client.config.ts
Sentry.init({
ignoreErrors: [
// Extensiones del navegador
/chrome-extension/,
/moz-extension/,
// Scripts de terceros
/google-analytics/,
// Hot reload en desarrollo
/HMR/,
],
denyUrls: [
// Ignorar errores de scripts de dominios concretos
/extensions\//i,
/^chrome:\/\//i,
],
});
2. Agrupar alertas repetidas
El mismo error solo una vez cada 10 minutos. Sentry «Issue Grouping» agrupa errores similares.
3. Período de silencio
Durante despliegues puede haber picos breves; «Mute for 10 minutes».
4. Fingerprint
Reglas personalizadas de agrupación:
Sentry.captureException(error, {
fingerprint: ['database-connection-error', databaseName],
});
Errores de conexión a distintas bases quedan separados para localizar mejor.
Caso práctico: despliegue de un plan de monitorización completo
Arquitectura de monitorización para un ecommerce
El año pasado ayudé a un ecommerce a renovar su monitorización. El esquema completo:
Contexto:
- ~80 000 UV/día
- QPS > 3000 en picos
- Problemas: fallos intermitentes en pago, home lenta
Arquitectura:
┌─────────────┐
│ Next.js │
│ frontend/SSR │
└──────┬──────┘
│
├─ Sentry (errores + rendimiento)
├─ Pino (logs estructurados) → Datadog
├─ Web Vitals → Sentry
└─ Checkly (monitorización sintética)
Configuraciones clave:
- Seguimiento de comportamiento del usuario
// lib/tracking.ts
import * as Sentry from '@sentry/nextjs';
export function trackCheckoutStep(step: string, data: any) {
Sentry.addBreadcrumb({
category: 'checkout',
message: `Checkout step: ${step}`,
data,
level: 'info',
});
}
// En el flujo de compra
trackCheckoutStep('add_to_cart', { productId, price });
trackCheckoutStep('proceed_to_payment', { cartTotal });
trackCheckoutStep('payment_submitted', { method: 'credit_card' });
Cuando falla un pago, ves el recorrido completo del usuario.
- Monitorización de pagos
// app/api/payment/route.ts
export async function POST(request: Request) {
const log = logger.child({ action: 'payment' });
try {
const result = await processPayment(data);
log.info({ orderId, amount, method }, 'Payment succeeded');
return Response.json({ success: true, orderId });
} catch (error) {
log.error({ error, orderId, userId }, 'Payment failed');
// Alerta P0
Sentry.captureException(error, {
tags: { feature: 'payment', severity: 'critical' },
level: 'fatal',
});
return Response.json({ error: 'Payment failed' }, { status: 500 });
}
}
Cualquier fallo de pago avisa al equipo al instante.
- Línea base de rendimiento
Con Sentry Performance Monitoring:
- LCP home < 2 s
- LCP ficha de producto < 2,5 s
- API /api/products P95 < 500 ms
Superar la línea base dispara alerta.
Resultados:
- Tiempo medio de detección de incidentes: de 40 min a 3 min
- Tasa de fallo en pago: de 0,8% a 0,2%
- LCP de home: de 3,2 s a 1,8 s tras optimización
Checklist de monitorización
Lista final para revisar tu proyecto:
**Seguimiento de errores**
- [ ] Sentry configurado y probado
- [ ] Source Maps subidos correctamente
- [ ] global-error.tsx creado (App Router)
- [ ] Server Actions con manejo de errores
- [ ] Reglas de ignorado configuradas (filtrar ruido)
**Gestión de logs**
- [ ] Librería de logs integrada (Pino/Winston)
- [ ] Producción en formato JSON
- [ ] Logs incluyen correlationId
- [ ] Nivel correcto (INFO en producción)
- [ ] Logs en plataforma de agregación
**Monitorización de rendimiento**
- [ ] Web Vitals activos
- [ ] Core Web Vitals dentro de objetivo (LCP<2.5s, INP<200ms, CLS<0.1)
- [ ] APIs críticas con trazas de rendimiento
- [ ] Monitorización de consultas lentas
- [ ] Monitorización sintética (opcional)
**Alertas**
- [ ] Slack/email probados
- [ ] Reglas por prioridad
- [ ] Reglas anti-ruido
- [ ] El equipo conoce el flujo de respuesta
- [ ] Responsable definido para P0
**Mejora continua**
- [ ] Revisión semanal de datos
- [ ] Análisis de tendencia de errores
- [ ] Detección de regresiones de rendimiento
- [ ] Optimización periódica de reglas de alerta
Conclusión
De apagar fuegos a detectar problemas antes: la monitorización te devuelve el control del entorno de producción.
Resumen del sistema que montamos:
- Sentry para errores y rendimiento, con replay de sesiones
- Pino para logs estructurados, encadenados por correlationId
- Web Vitals para métricas de experiencia y SEO
- Alertas en Slack para avisar al equipo al instante, con niveles para evitar fatiga
Sobre todo, un cambio de mentalidad: la monitorización no es un «extra nice to have», es el airbag de producción. No esperas a un accidente para instalar el airbag; tampoco deberías esperar a un incidente para montar monitorización.
Empieza hoy. Si tu proyecto aún no tiene nada:
- Este fin de semana, 2 horas para Sentry y seguimiento básico de errores
- La semana que viene, logs estructurados y correlationId
- La siguiente, alertas en Slack y línea base de rendimiento
No busques la perfección de golpe: primero la monitorización básica de errores, luego mejora paso a paso. Tras cada incidente pregúntate: «¿podría haberlo detectado antes?» Con el tiempo, la monitorización será tu mejor aliada.
Si este artículo te ayudó, compártelo con el equipo. La monitorización es cosa de todos, no de una sola persona.
Que tu app Next.js vaya estable como una roca. (Aunque la realidad a veces dice lo contrario — por eso la monitorización importa tanto 😄)
Flujo completo de configuración de monitorización en producción con Next.js
Pasos completos desde la integración de Sentry hasta logs, rendimiento y alertas
⏱️ Estimated time: 3 hr
- 1
Step 1: Integrar Sentry para seguimiento de errores
Instalación:
```bash
npm install @sentry/nextjs
```
Inicialización:
```bash
npx @sentry/wizard@latest -i nextjs
```
Configuración del cliente:
```ts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Configuración del servidor:
```ts
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Puntos clave:
• Cliente y servidor se configuran por separado
• Ajusta tracesSampleRate para controlar la tasa de muestreo
• Configura las variables de entorno - 2
Step 2: Configurar logs estructurados
Usa correlationId para vincular peticiones:
```ts
// middleware.ts
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const correlationId = request.headers.get('x-correlation-id') || uuidv4()
const response = NextResponse.next()
response.headers.set('x-correlation-id', correlationId)
return response
}
```
En los logs:
```ts
import { headers } from 'next/headers'
export async function handler() {
const headersList = headers()
const correlationId = headersList.get('x-correlation-id')
console.log({
correlationId,
message: 'User action',
timestamp: new Date().toISOString(),
})
}
```
Puntos clave:
• Genera un ID único por petición
• Incluye correlationId en todos los logs
• Facilita rastrear toda la cadena de la petición - 3
Step 3: Configurar monitorización de rendimiento
Sentry APM:
```ts
Sentry.init({
tracesSampleRate: 1.0, // 100% de muestreo
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
})
```
Monitorización personalizada:
```ts
const transaction = Sentry.startTransaction({
op: 'http.server',
name: 'API Route',
})
try {
// lógica de negocio
await processRequest()
} finally {
transaction.finish()
}
```
Puntos clave:
• Ajusta tracesSampleRate para controlar el muestreo
• Monitoriza tiempos de respuesta de la API
• Identifica cuellos de botella - 4
Step 4: Configurar alertas
Alertas en Sentry:
• Configura reglas de alerta en el Sentry Dashboard
• Define umbrales de error
• Configura canales de notificación (Slack, email, etc.)
Integración con Slack:
```ts
// Configurar en el Sentry Dashboard
// Webhook URL: https://hooks.slack.com/services/...
```
Alertas por email:
• Configurar en el Sentry Dashboard
• Definir destinatarios
• Configurar condiciones de alerta
Puntos clave:
• Umbrales razonables
• Evita la fatiga de alertas
• Responde a tiempo
FAQ
¿Por qué Next.js necesita un plan de monitorización específico?
La misma aplicación corre en tres lugares distintos:
• Cliente (Browser): componentes React en el navegador del usuario
• Servidor (Node.js): renderizado SSR, API Routes, Server Actions
• Red perimetral (Edge Runtime): middleware, funciones edge
La monitorización frontend tradicional solo ve errores del cliente, no del servidor ni del edge.
Efecto caja negra del SSR:
• Cuando falla el renderizado en servidor, el usuario solo ve una página 500
• Sin stack trace ni contexto
• Hace falta el rastreo distribuido de Sentry para detectar el problema
Caso real:
• Un usuario reporta: la página carga muy lento y luego muestra 500
• El panel Network del navegador muestra peticiones lentas, pero no dónde
• Tras integrar Sentry se descubrió que una API de terceros pasó de 200 ms a 8 segundos en el servidor
Solución: un sistema de monitorización completo que cubra cliente, servidor y edge.
¿Cómo integrar Sentry?
```bash
npm install @sentry/nextjs
```
Inicialización:
```bash
npx @sentry/wizard@latest -i nextjs
```
Configuración del cliente:
```ts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Configuración del servidor:
```ts
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```
Puntos clave:
• Cliente y servidor por separado
• tracesSampleRate controla el muestreo
• Variables de entorno (NEXT_PUBLIC_SENTRY_DSN, SENTRY_DSN)
¿Cómo configurar logs estructurados?
Generar en middleware:
```ts
import { v4 as uuidv4 } from 'uuid'
export function middleware(request: NextRequest) {
const correlationId = request.headers.get('x-correlation-id') || uuidv4()
const response = NextResponse.next()
response.headers.set('x-correlation-id', correlationId)
return response
}
```
En los logs:
```ts
import { headers } from 'next/headers'
export async function handler() {
const headersList = headers()
const correlationId = headersList.get('x-correlation-id')
console.log({
correlationId,
message: 'User action',
timestamp: new Date().toISOString(),
})
}
```
Ventajas:
• ID único por petición
• Todos los logs llevan correlationId
• Rastreo de toda la cadena
• Localización rápida de problemas
Clave: generar en middleware, usar en logs, facilitar la correlación.
¿Cómo configurar monitorización de rendimiento?
```ts
Sentry.init({
tracesSampleRate: 1.0, // 100% de muestreo (en producción se recomienda 0.1)
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
})
```
Monitorización personalizada:
```ts
const transaction = Sentry.startTransaction({
op: 'http.server',
name: 'API Route',
})
try {
// lógica de negocio
await processRequest()
} finally {
transaction.finish()
}
```
Métricas:
• Tiempo de respuesta de la API
• Tiempo de consultas a base de datos
• Tiempo de llamadas a APIs de terceros
• Tiempo de carga de página
Puntos clave:
• tracesSampleRate (en producción se recomienda 0.1)
• Monitoriza rutas críticas
• Identifica cuellos de botella
¿Cómo configurar alertas?
• Reglas de alerta en el Sentry Dashboard
• Umbrales de error (p. ej.: más de 10 errores en 5 minutos)
• Canales de notificación (Slack, email, etc.)
Integración con Slack:
• Webhook URL en el Sentry Dashboard
• Condiciones de alerta
• Probar alertas
Alertas por email:
• Configurar en el Sentry Dashboard
• Destinatarios
• Condiciones de alerta
Reglas recomendadas:
• Tasa de error por encima del umbral
• Tiempo de respuesta por encima del umbral
• Tipos de error concretos
• Errores nuevos
Puntos clave:
• Umbrales razonables
• Evita fatiga de alertas
• Responde a tiempo
Consejo: primero la monitorización básica de errores, luego ve mejorando.
¿Cuáles son las mejores prácticas de monitorización?
1. Este fin de semana, 2 horas para integrar Sentry y seguimiento básico de errores
2. La semana que viene, logs estructurados y correlationId
3. La siguiente, alertas en Slack y línea base de rendimiento
No busques hacerlo todo de golpe; primero la monitorización básica de errores.
Mejora continua:
• Tras cada incidente: ¿podría la monitorización haberlo detectado antes?
• Ajusta umbrales según la realidad
• Revisa la configuración periódicamente
Métricas clave:
• Tasa de error
• Tiempo de respuesta
• Alcance del impacto en usuarios
• Tiempo de recuperación
Recomendaciones:
• La monitorización es cosa del equipo, no de una sola persona
• Comparte datos de monitorización con regularidad
• Mejora el sistema de forma continua
Recuerda: la monitorización no es una tarea puntual, sino un proceso continuo.
18 min de lectura · Publicado el: 20 dic 2025 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Escapar de Vercel: guía completa de autoalojamiento de Next.js con Docker
¿Cansado de las facturas altas de Vercel? Esta guía te enseña a autoalojar Next.js con Docker: configuración standalone, proxy inverso y solución para el streaming, ahorrando $300-500 al mes.
Parte 42 de 51
Siguiente
Modo oscuro en Next.js: guía completa con next-themes
Del parpadeo al cero flash: implementa modo oscuro en Next.js con next-themes paso a paso. Código completo, cómo funciona por dentro y solución de problemas habituales.
Parte 44 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario