Patrones de registro estructurado para depurar API de Node.js de manera efectiva
Sustituir las llamadas dispersas a console.log en las APIs de Node.js por un registro estructurado: contexto de la solicitud, IDs de solicitud, niveles de registro y métricas de tiempo para una depuración más rápida.
Fuentes: I Stopped Using Console.log() to Debug Node.js APIs
Un proyecto de backend que en un principio dependía de instrucciones de impresión dispersas terminó convirtiéndose en una lección sobre por qué ese enfoque deja de funcionar a medida que los sistemas crecen.
Un hábito común al principio es el siguiente:
console.log("HERE 1");
const user = await getUser(userId);
console.log("HERE 2", user);
const orders = await getOrders(userId);
console.log("HERE 3", orders);
console.log("HERE 4");
Luego se ejecuta la API y se observa la terminal, esperando encontrar pistas.
A veces funciona.
Otras veces no.
Y cuando algo falla, el instinto es agregar otro console.log().
Luego otro más.
Y otro aún.
Con el tiempo, la terminal se llena de algo como esto:
HERE 1
HERE 2
HERE 2
HERE 3
HERE 1
HERE 4
HERE 3
Pero aún así no se está más cerca de entender qué sucedió realmente.
Esa frustración es lo que suele llevar a las personas a replantearse cómo depuran los sistemas de backend.
1. El problema con los registros aleatorios
console.log() en sí no es el problema.
Es una herramienta realmente útil.
El verdadero problema es usarlo sin ninguna estructura.
Tomemos este ejemplo:
console.log("user", user);
console.log("order", order);
console.log("data", data);
console.log("result", result);
Apenas unos minutos después de ejecutar la aplicación, uno se encuentra frente a cientos de líneas.
Luego surgen las preguntas reales:
¿Qué solicitud generó esta línea de registro en particular?
¿Qué usuario provocó el fallo?
¿Cuánto tiempo duró realmente la llamada a la base de datos?
¿Qué endpoint estaba en ejecución en ese momento?
Los registros simples y sin estructura no pueden responder a ninguna de estas preguntas.
2. Iniciar el registro con contexto
En lugar de escribir:
console.log("Payment failed");
es mucho más útil escribir:
console.error({
level: "error",
message: "Payment failed",
userId,
orderId,
endpoint: req.originalUrl,
})
Ahora la entrada de registro incluye un contexto real.
Por ejemplo, podría verse así:
{
"level": "error",
"message": "Payment failed",
"userId": "user_123",
"orderId": "order_456",
"endpoint": "/api/orders/payment"
}
Ese contexto adicional convierte la depuración de una tarea basada en conjeturas en algo manejable.
3. Los IDs de solicitud cambiaron todo
Imagínese una API que maneja cientos de solicitudes por segundo.
Sus registros podrían mostrar:
Database timeout
Payment failed
Redis error
User created
Database timeout
Pero, ¿qué solicitud causó cada uno de estos errores?
No hay forma de saberlo.
Es exactamente aquí donde los IDs de solicitud son útiles.
Algo como:
req_8f92a1
Cada línea de registro relacionada con esa solicitud específica puede compartir el mismo identificador.
req_8f92a1 → Request started
req_8f92a1 → User authenticated
req_8f92a1 → Database query started
req_8f92a1 → Database query completed
req_8f92a1 → Payment API called
req_8f92a1 → Payment failed
Con esto en su lugar, puede seguir una solicitud de principio a fin, sin importar cuán ocupado esté el sistema.
4. Mida cuánto tiempo toman las cosas
Una entrada de registro que solo dice:
Database query completed
no cuenta toda la historia.
Lo que realmente quiere es algo como:
Database query completed in 1,842ms
Una forma sencilla de capturar eso:
const start = Date.now();
const users = await getUsers();
console.log({
operation: "getUsers",
duration: Date.now() - start
});
Una vez que comience a medir, podría ver algo como:
getUsers → 38ms
getOrders → 1,923ms
getProducts → 42ms
De repente, la parte lenta de su sistema se hace evidente claramente, en lugar de permanecer oculta detrás de una queja vaga como:
"La API parece lenta."
5. No registre todo
Este es un error que merece ser señalado directamente.
La suposición anterior era:
"Más registros facilitan la depuración."
Eso no es del todo cierto.
El registro excesivo solo añade ruido.
Por ejemplo:
console.log("Entering function");
console.log("User found");
console.log("Checking order");
console.log("Order found");
console.log("Checking product");
console.log("Product found");
console.log("Returning response");
La mayoría de estas líneas no aportan nada cuando la aplicación funciona normalmente.
Es mejor centrarse únicamente en los eventos que realmente importan:
Request started
Request completed
Authentication failed
Database error
External API failure
Important business event
Unexpected exceptio
El objetivo no es registrar cada cosa que sucede.
El objetivo es hacer que los eventos importantes sean fáciles de encontrar.
6. Utilice niveles de registro
Los sistemas de producción deben separar los registros según su nivel de gravedad.
Típicamente eso significa:
DEBUG
INFO
WARN
ERROR
DEBUG
Detalles muy detallados destinados al desarrollo, como:
SQL query parameters
Cache lookup details
Internal state
INFO
Eventos habituales que ocurren durante el funcionamiento normal:
Server started
User logged in
Order created
Payment completed
WARN
Ocurrió algo inusual, pero la aplicación siguió funcionando:
Cache unavailable
Retrying external API
Slow database query
ERROR
Algo realmente falló:
Database connection failed
Payment failed
Unhandled exception
Esta distinción se vuelve especialmente valiosa cuando los registros fluyen hacia un sistema centralizado de registro.
7. Evite filtrar secretos en sus registros
Este punto merece atención adicional.
Nunca escriba descuidadamente algo como:
console.log(req.body);
porque el cuerpo de la solicitud recibida podría contener fácilmente algo como:
{
"email": "user@example.com",
"password": "secret",
"token": "..."
}
Si lo registra sin pensar, acaba de escribir datos sensibles directamente en sus archivos de registro.
Tenga cuidado con campos como:
- Contraseñas
- JWTs
- Claves API
- Tokens de acceso
- Detalles de pago
- Información personal
En lugar de registrar todo el contenido, decida deliberadamente qué información merece ser capturada.
8. Registrar errores de la manera correcta
Escribir esto:
catch (error) {
console.log(error);
}
no le proporciona casi nada con lo que trabajar en entornos de producción.
Necesita un contexto adyacente que haga que el error tenga sentido.
Algo más similar es mucho más útil:
catch (error) {
logger.error({
message: "Failed to create order",
error: error.message,
stack: error.stack,
userId,
orderId
});
throw error;
}
Con ese enfoque, captura:
- Lo que sucedió
- El mensaje de error
- La traza de pila
- Cualquier identificador relevante
Esa combinación convierte una línea de registro en algo con lo que realmente se puede actuar.
9. Depuración de la capa de base de datos
Supongamos que nuestra API se ha vuelto lenta.
En lugar de registrar algo tan vago como:
API is slow
busque algo más similar a:
GET /api/orders
Total: 2,431msAuthentication: 24ms
Database: 2,312ms
Serialization: 31ms
Esa desglose le indica exactamente dónde buscar a continuación.
A partir de ahí, puede analizar la consulta en sí.
Puede descubrir algo como:
Sequential Scan
Los culpables más comunes incluyen:
- Un índice faltante
- Un patrón de consulta N+1
- Una unión mal optimizada
- Obtener muchos más datos de los necesarios
La idea fundamental es que la observabilidad reemplaza las conjeturas por pruebas.
10. Depuración de llamadas a servicios externos
Imagínese una aplicación Node.js que depende de varios servicios externos:
Payment API
Shipping API
Email API
WhatsApp API
De repente, una solicitud tarda 8 segundos en completarse.
¿Cuál de esos servicios es el responsable?
Sin datos de tiempo, solo se está especulando.
En su lugar, registre algo como:
Request started
Payment API: 142ms
Shipping API: 6,821ms
Database: 81msRequest completed: 7,100ms
Ahora el cuello de botella es evidente.
El propio servidor Node.js podría estar funcionando perfectamente.
La lentitud proviene del proveedor de envíos.
Por eso es tan importante medir el tiempo de ejecución de las dependencias externas.
11. Los buenos registros responden a preguntas específicas
Cuando hay problemas en producción, quiere que sus registros le permitan saber:
¿Qué sucedió?
Payment request failed
¿Cuándo sucedió?
2026-09-16T10:15:23Z
¿Qué solicitud?
req_8f92a1
¿Qué usuario?
user_123
¿Qué endpoint?
POST /api/orders/payment
¿Qué dependencia falló?
payment-provider
¿Cuánto tiempo tomó?
5,002ms
Con todo eso presente, la entrada de registro finalmente cumple su función.
12. El registro de logs es solo una parte de la observabilidad
Esa fue otra área donde el modelo mental inicial estaba equivocado.
La observabilidad no es simplemente:
console.log()
Un sistema en producción generalmente depende de tres pilares que funcionan juntos:
Logs
+
Metrics
+
Traces
Registros
Ellos responden:
¿Qué sucedió?
Métricas
Ellos responden:
¿Con qué frecuencia está sucediendo?
Por ejemplo:
Error rate: 2.4%
Average latency: 820ms
CPU: 75%
Rastreos
Ellos ayudan a responder:
¿Dónde se fue realmente el tiempo en los diferentes servicios involucrados?
Por ejemplo:
Request
↓ 20ms
API
↓ 80ms
Database
↓ 1,200ms
Shipping Service
↓ 50ms
Response
Juntos, estos tres elementos brindan una visión mucho más clara de lo que ocurre dentro de un sistema en producción.
13. Creación de un registrador mínimo
Incluso un registrador estructurado y ligero puede marcar una diferencia notable en un servicio pequeño de Node.js.
Conceptualmente, podría verse así:
logger.info({
event: "order_created",
orderId,
userId
});
En comparación con:
console.log(
"order created",
orderId,
userId
);
La versión estructurada es mucho más fácil de buscar, filtrar y agrupar posteriormente con las herramientas adecuadas.
También se pueden reenviar estas entradas a una plataforma centralizada de registro en el futuro.
14. El flujo de trabajo para depurar ahora
El proceso general de depuración se ha vuelto mucho más sencillo.
En lugar del patrón anterior:
Something is broken
↓
Add console.log()
↓
Run again
↓
Add another console.log()
↓
Run again
el flujo de trabajo ahora se ve así:
Something is broken
↓
Identify the failing request
↓
Find the request ID
↓
Follow the logs
↓
Measure dependencies
↓
Find the bottleneck
↓
Fix it
↓
Verify with measurements
Es un enfoque fundamentalmente diferente para abordar el problema.
15. La pregunta más importante
Cuando te enfrentes a un problema en producción, evita preguntarte:
"¿Qué código debo cambiar?"
Pregunte en su lugar:
"¿Qué pruebas tengo realmente sobre qué está fallando?"
Esa forma de plantearlo lo pone en una posición mucho más fuerte.
Si aún no sabe dónde está el problema, cambiar código no es más que una conjetura.
Lista de verificación para el registro en el backend
Antes de lanzar un servicio de backend, es útil confirmar:
[ ] Structured logging
[ ] Request IDs
[ ] Error logs
[ ] Stack traces
[ ] Request duration
[ ] Database duration
[ ] External API duration
[ ] Useful business events
[ ] Log levels
[ ] Sensitive data excluded
[ ] Centralized logs
[ ] Error monitoring
[ ] Basic metrics
[ ] Health checks
Consideraciones finales
console.log() sigue teniendo su lugar.
No es algo que deba evitarse por completo.
El verdadero problema es depender de él como la única herramienta de depuración.
Un backend en producción necesita algo más que:
console.log("something happened");
Necesita tener visibilidad de:
What happened?
When?
Where?
Why?
For which request?
For which user?
How long did it take?
Which dependency was involved?
Una vez que esa mentalidad se instala, la depuración deja de consistir en modificar el código al azar y comienza a centrarse en seguir las pruebas que se tienen delante.
El cambio más importante en el enfoque se resume en una idea:
No agregues más registros solo porque algo falló.
En lugar de eso, captura los detalles adecuados para que, la próxima vez que algo salga mal, puedas realmente entender qué sucedió.
Porque en producción:
Si no puedes ver qué está haciendo tu sistema, estás depurando a ciegas.
Lecturas relacionadas
- Registro Estructurado en Node.js: Conviertiendo el Caos de la Deboguación en Producción en Claridad — Aprenda por qué console.log falla en aplicaciones Node.js en producción y cómo el registro estructurado, los niveles de registro y los IDs de correlación convierten errores complejos en soluciones rápidas.
- Cinco Errores Engañosos en Node.js que Superan la Revisión de Código sin Ser Detectados — Conozca cinco errores reales en Node.js relacionados con forEach, promesas flotantes y copias superficiales para entender por qué el código que funciona bien aún puede fallar en producción.