Logs y Observabilidad
Logging estructurado para Node, Edge y Workers con IDs de solicitud, redacción de secretos y ejemplos por ruta. Funciona en Vercel, Cloudflare y servidores Node.
Conceptos básicos
- Logs estructurados: emite objetos JSON, no cadenas sueltas; facilita buscar y correlacionar.
- IDs de correlación: añade
request_ida todos los logs de una solicitud. - Niveles:
debugen dev,infonormal,warnanomalías,errorfallos. - Redacción: nunca registres secretos, tokens o cookies.
Qué incluye
- API de logging común para Node y Edge/Workers (misma forma).
- Node (funciones de servidor Next, serverless, Node): Pino rápido con JSON.
- Edge/Workers (Vercel Edge, Cloudflare Workers, middleware): implementación ligera con
console.logJSON.
- Propagación de
request_idvía middleware (sin tocar cookies). - Ejemplo conectado en la ruta de presign de almacenamiento.
Archivos
- Node:
src/lib/logger/server.ts - Edge:
src/lib/logger/edge.ts - Tipos:
src/lib/logger/types.ts - Middleware:
src/middleware.ts - Ejemplo:
src/app/api/storage/uploads/route.ts
Archivos clave
- Node:
src/lib/logger/server.ts - Edge:
src/lib/logger/edge.ts - Tipos:
src/lib/logger/types.ts - Middleware:
src/middleware.ts - Ejemplo:
src/app/api/storage/uploads/route.ts
Dónde ver los logs
- Local: consola del terminal (salida JSON de una línea). Para legibilidad usa
pino-pretty:pnpm dev 2>&1 | pnpx pino-pretty- guardar en archivo:
pnpm dev 2>&1 | pnpx pino-pretty | tee logs/dev.log
- Vercel: Deployments → Functions → Logs, o
vercel logs. - Cloudflare Workers:
wrangler tailo panel de Logs.
Uso (Node)
import { logger, requestIdFromHeaders } from '@/lib/logger/server'
export async function POST(req: Request){
const rid = requestIdFromHeaders(req.headers)
const log = logger.child({ request_id: rid, route: '/api/ejemplo' })
log.info({ event: 'ejemplo.start' })
// ...
log.info({ event: 'ejemplo.ok' })
return new Response('ok')
}Uso (Edge)
import { logger } from '@/lib/logger/edge'
export const runtime = 'edge'
export function GET(){
logger.info({ event: 'edge.heartbeat' })
return new Response('ok')
}Config
LOG_LEVELy reglas de redacción en los archivos del logger.- No hay archivo de logs por defecto: escribimos a stdout/stderr. En local, usa
teesi quieres guardar:pnpm dev 2>&1 | tee logs/dev.json.
Uso (Node)
import { logger, requestIdFromHeaders } from '@/lib/logger/server'
export async function POST(req: Request){
const rid = requestIdFromHeaders(req.headers)
const log = logger.child({ request_id: rid, route: '/api/ejemplo' })
const start = Date.now()
try {
log.info({ event: 'ejemplo.start' })
// ...
log.info({ event: 'ejemplo.ok', duration_ms: Date.now() - start })
return new Response('ok')
} catch (e:any) {
log.error({ event: 'ejemplo.error', message: e?.message })
return new Response('error', { status: 500 })
}
}Uso (Edge)
import { logger } from '@/lib/logger/edge'
export const runtime = 'edge'
export function GET(){
logger.info({ event: 'edge.heartbeat' })
return new Response('ok')
}Helper: withApiLogging (Node)
import { withApiLogging } from '@/lib/logger/server'
export const POST = withApiLogging(async (req) => {
// lógica
return new Response('ok')
}, { route: '/api/foo', event: 'foo.process' })Ejemplo ya conectado
src/app/api/storage/uploads/route.tsemitestorage.presign.createcon{ request_id, user_id, file_id, key, size, content_type, bucket }y erroresstorage.presign.create.error.
Buenas prácticas
- Usa
request_iden loggers hijo por petición y añadeduration_msal finalizar. - Mensajes cortos; prioriza campos.
- Evita registrar cuerpos completos; mejor hashes/campos puntuales.
Redacción a medida
- Node: edita
redactPathsensrc/lib/logger/server.ts(Pino reemplaza por[REDACTED]). - Edge: edita
redactKeysensrc/lib/logger/edge.ts.
Sustituir console.*
- En
src/app/api/**ysrc/services/**, importalogger(Node) y usalogger.info|warn|error. - En cliente, limita
consoley usa un capturador de errores (p. ej., Sentry).
Integraciones opcionales
- Sentry (
@sentry/nextjs) para excepciones y trazas. - Sinks gestionados (Axiom, Better Stack, Datadog, New Relic):
- Node: transport/Pino o captura stdout.
- Edge: ingesta HTTP o logs de plataforma.
Solución de problemas
- Redirecciones inesperadas a login: asegúrate de que el middleware no sobrescribe cabeceras de solicitud/cookies.
- Falta de logs en Edge: verifica
runtime = 'edge'y usawrangler tail/Vercel logs.
Buenas prácticas
- Usa
request_idyduration_ms; evita cuerpos completos. - Sustituye
console.*por el logger en código del servidor.
Cargas de archivos privadas (S3 / R2)
Guía paso a paso, apta para principiantes, para añadir cargas privadas por usuario con almacenamiento compatible con S3. Incluye conceptos, configuración, variables de entorno, API, UI, errores y migración S3↔R2.
Servicio de Email (Resend)
Integra email transaccional con Resend. Verifica tu dominio, crea claves API, renderiza plantillas de bienvenida y pago en el servidor y envíalas a través de Resend con configuración por entorno.