Cuando tu API cae a las 3 de la mañana, abres los logs y ves: "Error processing request". Sin request ID, sin timestamp estandarizado, sin contexto de lo que estaba pasando. Ese log no ayuda a resolver nada.
El logging estructurado resuelve esto: cada log es un objeto JSON con campos estandarizados que las herramientas de observabilidad pueden indexar, filtrar y correlacionar.
El formato que funciona
Cada log debe ser una línea JSON con campos consistentes:
{
"level": "error",
"timestamp": "2026-12-18T14:30:00.123Z",
"message": "Failed to process payment",
"service": "payment-api",
"request_id": "req_abc123",
"user_id": "usr_xyz789",
"error": {
"name": "PaymentGatewayError",
"message": "Card declined",
"stack": "PaymentGateway.ts:45:12"
},
"context": {
"amount": 15000,
"currency": "BRL",
"payment_method": "credit_card"
}
}
Compáralo con el log no estructurado: "Error al procesar pago". El formato estructurado permite buscar "todos los errores de pago del usuario xyz789", o "errores de tarjeta rechazada en los últimos 5 minutos", o "cuál es la tasa de fallo del gateway X".
Los campos que importan
- level: error, warn, info, debug. Estandarízalo. Nunca uses console.log en producción.
- timestamp: ISO 8601, siempre en UTC. Evita problemas de timezone.
- service: qué microservicio generó el log. Obligatorio en arquitectura distribuida.
- request_id: ID único del request. Se propaga entre servicios. Permite correlacionar logs del mismo flujo.
- user_id: cuando aplique, el ID del usuario afectado.
- error: objeto con nombre, mensaje y stack trace.
- context: datos específicos de la operación que ayudan en el debug.
Implementación con pino (Node.js)
Pino es el logger estándar para Node.js en producción. Rápido, estructurado y con soporte nativo de JSON:
import pino from 'pino'
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
timestamp: pino.stdTimeFunctions.isoTime,
})
// Middleware de request
app.use((req, res, next) => {
req.id = crypto.randomUUID()
req.log = logger.child({
request_id: req.id,
method: req.method,
url: req.url,
})
const start = Date.now()
res.on('finish', () => {
req.log.info({
status_code: res.statusCode,
duration_ms: Date.now() - start,
}, 'Request completed')
})
next()
})
Request ID: el hilo conductor
El request_id es el campo más importante para debug en sistemas distribuidos. Necesita nacer en la primera capa y propagarse por todas las llamadas:
// Generar en el gateway/API
const requestId = req.headers['x-request-id'] || crypto.randomUUID()
// Propagar en llamadas internas
await fetch('http://user-service/api/users', {
headers: { 'X-Request-Id': requestId }
})
Con herramientas como Datadog, Elasticsearch o Loki, puedes buscar todos los logs de un request_id específico y ver el flujo completo de un request, de punta a punta.
Qué loguear y qué no loguear
Loguear: errores, warnings, eventos de negocio importantes (pago aprobado, cuenta creada), métricas de performance (duración de queries, tiempo de respuesta).
No loguear: datos sensibles (contraseñas, tokens, datos de tarjeta), datos PII sin necesidad, flujo normal de operaciones (excesivo), variables de entorno.
// ❌ Nunca
logger.info({ password: user.password, token: jwt })
// ✅ Correcto
logger.info({ user_id: user.id, action: 'login', ip: req.ip })
Log levels en la práctica
Error: algo falló y necesita atención. Alerta inmediata.
Warn: algo inesperado pero no crítico. Merece investigación.
Info: eventos normales del sistema. Pago procesado, email enviado.
Debug: detalles para desarrollo. Deshabilitado en producción.
En producción, corre con level: 'info'. Activa debug temporalmente vía variable de entorno cuando necesites investigar algo específico.
Correlación con métricas
Logs estructurados combinados con métricas (Prometheus, Datadog) permiten dashboards que muestran: tasa de errores por endpoint, latencia percentil, throughput por servicio. El log da el qué pasó, la métrica da el cuánto.
¿Te gustó el contenido?
Construyo productos web y soluciones con IA de la manera correcta — arquitectura sólida, código sostenible y entrega real.
Hablemos