Inicio / Artículos

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.

Editor: REACTAPP.TOP8 min de lectura1883 palabras

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