Usá el módulo debug de Node.js para logs condicionales por namespace

DUGLAS MORENODUGLAS MORENO 👁 4

Introducción

El desarrollo de aplicaciones Node.js a menudo genera una gran cantidad de datos de depuración y registro. Saber qué información imprimir en producción, qué dejar para desarrollo y cómo organizar esos mensajes puede marcar la diferencia entre una experiencia de depuración fluida y una sesión interminable de búsqueda de errores.

El módulo debug es una herramienta integrada en Node.js que facilita exactamente eso: registros condicionales por namespace. Al etiquetar cada mensaje con un identificador, podés controlar con precisión qué logs aparecen, sin necesidad de envolver cada console.log en condiciones.

Este artículo explica cómo configurar namespaces, usar el módulo en aplicaciones reales y aplicar prácticas recomendadas para mantener tus logs claros y útiles. Aprenderás con ejemplos concretos y verás la salida esperada en cada caso.

¿A quién va dirigido este artículo?

  • Desarrolladores Node.js que necesitan un control más granular sobre los mensajes de depuración.
  • Entregas que trabajan con microservices y APIs donde diferentes componentes requieren niveles de detalle distintos.
  • Equipos que buscan estandarizar la forma de escribir logs para facilitar el trabajo en conjunto.

Al final del artículo, sabrás:

  1. Cómo definir y organizar namespaces.
  2. Cómo activar o desactivar grupos de logs usando variables de entorno.
  3. Cómo integrar el módulo debug en código modular y en la línea de comandos.
  4. Las buenas prácticas y errores frecuentes al trabajar con el módulo.

1. Conceptos básicos del módulo debug

El módulo debug es parte del núcleo de Node.js (no necesita instalación). Exporta una función que acepta un namespace y devuelve una función de logging que imprime solo cuando el namespace está habilitado.

const debug = require('debug');

// Definir un logger para el servidor
const serverDebug = debug('myapp:server');
// Definir un logger para la base de datos
const dbDebug = debug('myapp:db');

serverDebug('Iniciando el servidor');
dbDebug('Conectando a PostgreSQL');

Salida esperada (cuando DEBUG=myapp:server,myapp:db):

myapp:server Iniciando el servidor
myapp:db Conectando a PostgreSQL

Si la variable de entorno DEBUG no incluye esos namespaces, la salida es silenciosa:

$ node example.js
// (no hay nada impreso)

1.1 Cómo funciona el sistema de namespaces

  • Namespace: Un string que identifica lógicamente una fuente de logs (ej: auth, api, database). Podés anidar con : para crear jerarquías (myapp:auth, myapp:auth:login).
  • Activación: El módulo usa la variable de entorno DEBUG. Podés proveer un solo namespace, una lista separada por comas, o patrones con * (ej: myapp:*).
  • Etiqueta: Cada mensaje impreso comienza con el namespace seguido de un espacio, luego el texto.

1.2 Instalación y versión

Como está integrado, podés verificarlo inmediatamente:

console.log(require('debug').version); // ej: '3.2.1'

Si necesitás una versión más nueva (rara vez), podés actualizar con npm install debug@latest, pero la mayoría de los proyectos Node.js ya lo incluyen.


2. Configurando namespaces efectivos

2.1 Variables de entorno DEBUG

El comportamiento del módulo se controla completamente mediante DEBUG. Algunos ejemplos útiles:

Comando (línea de comandos) Efecto
DEBUG=myapp:server Solo los logs con myapp:server aparecen.
DEBUG=myapp:* Todas las etiquetas bajo myapp: (incluidos sub‑namespaces).
DEBUG=myapp:server myapp:db (espacio) También funciona; espacios en lugar de comas.
DEBUG=0 Deshabilita todos los logs (útil para producción).

Puedes exportar estas variables en scripts package.json:

"scripts": {
  "start:dev": "DEBUG=myapp:* node index.js",
  "start:prod": "node index.js"
}

2.2 Creando una convención de nombres

Una convención clara ayuda a todo el equipo a saber qué namespace usar:

  • Dominio de la aplicación: myapp
  • Componente: myapp:auth, myapp:api, myapp:worker
  • Sub‑acción: myapp:auth:login, myapp:auth:logout
  • Lenguaje: Siempre en minúsculas, separado por :.

Seguir esta regla hace que los patrones como myapp:* sean significativos.

2.3 Ejemplo de salida filtrada

const debug = require('debug');

const info = debug('myapp:api');
const warn = debug('myapp:api:warning');

info('Solicitud recibida');
warn('Recurso no encontrado');

Con DEBUG=myapp:api:

myapp:api Solicitud recibida

Con DEBUG=myapp:api:warning:

myapp:api:warning Recurso no encontrado

3. Ejemplos prácticos

3.1 Logger básico para un servidor HTTP

// server.js
const debug = require('debug')('myapp:server');
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  debug(`Solicitud recibida: ${req.url}`);
  res.end('OK');
});

server.listen(port, hostname, () => {
  debug(`Servidor escuchando en http://${hostname}:${port}/`);
});

Salida esperada (ejecutando DEBUG=myapp:server node server.js):

myapp:server Solicitud recibida: /
myapp:server Servidor escuchando en http://127.0.0.1:3000/

Si ejecutás sin la variable, no hay logs:

$ node server.js
# (sin impresión)

3.2 Diferentes niveles con el mismo namespace

A pesar de que debug no impone niveles, podés prefijar los mensajes manualmente o usar un wrapper sencillo:

const debug = require('debug')('myapp:auth');

function logInfo(msg) { debug(`[INFO] ${msg}`); }
function logError(msg) { debug(`[ERROR] ${msg}`); }

logInfo('Usuario autenticado');
logError('Contraseña inválida');

Salida esperada (DEBUG=myapp:auth):

myapp:auth [INFO] Usuario autenticado
myapp:auth [ERROR] Contraseña inválida

3.3 Integraciones en aplicaciones modulares

Cuando tu app tiene varios módulos, cada uno puede definir su propio logger:

// auth.js
const debug = require('debug')('myapp:auth');
module.exports = {
  login: (user) => debug(`Intento de login para ${user}`),
  logout: () => debug('Usuario deslogueado')
};

// api.js
const debug = require('debug')('myapp:api');
const auth = require('./auth');

debug('Iniciando servidor API');
auth.login('alice');
auth.logout();

Salida esperada (DEBUG=myapp:auth,myapp:api):

myapp:api Iniciando servidor API
myapp:auth Intento de login para alice
myapp:auth Usuario deslogueado

Si solo activás myapp:api, los logs de auth se ocultan automáticamente.

3.4 Logs a stderr vs stdout

Por defecto, debug escribe en process.stdout. Si necesitas que los logs de depuración vayan a stderr (útil para redirigir la salida estándar), podés sobreescribir debug.log:

const debug = require('debug')('myapp:worker');
debug.log = console.error.bind(console); // redirigir a stderr

debug('Ejecutando tarea intensiva en CPU');

Salida (redirigida a stderr, cuando DEBUG=myapp:worker):

myapp:worker Ejecutando tarea intensiva en CPU

(Será visible incluso si redirigís stdout a un archivo.)


4. Buenas prácticas

  1. Elegí un estilo de namespace consistente. Usá el formato app:subsystem:action. Esto facilita filtrar con *.
  2. Activa solo lo necesario en producción. Un ejemplo útil: DEBUG=myapp:api en un entorno de staging, y DEBUG= (vacío) en producción.
  3. Evita imprimir datos sensibles. Incluso si un namespace está desactivado, still podés tener claves, tokens, contraseñas en texto plano; sanitizalos antes de loguearlos.
  4. Agregá un nivel (INFO/ERROR) explícitamente. El módulo no impone niveles, así que añadir [INFO] ayuda a filtrar visualmente.
  5. Usá colores en la terminal. La biblioteca debug agrega colores automáticamente si la salida es un tty. No intentes sobrescribir debug.log con un wrapper que quite colores a menos que sea necesario.
  6. Documentá los namespaces en README.md. Incluí un pequeño resumen de qué logs producirá cada namespace.
  7. Versioná los cambios en DEBUG. Los equipos a menudo usan un .env.example para mantener constancia de las variables.

5. Errores comunes y cómo evitarlos

Error Por qué es problemático Solución
Olvidar exportar DEBUG en producción Los logs aparecen donde no deberían, llenando archivos de log. Configurá DEBUG= (vacío) en contenedores o CI.
Usar espacios en lugar de comas (o viceversa) DEBUG=myapp:server myapp:db funciona, pero myapp:server,myapp:db es más portable. Decide un estilo y mantenlo consistente en scripts.
Usar mayúsculas en namespaces Los colores y herramientas pueden tratar mayúsculas como un namespace distinto. Siempre en minúsculas.
Anidar excesivamente (myapp:server:request:body:field) Dificulta filtrar jerárquicamente. Mantené la profundidad limitada (máx 2-3 niveles).
Sobreescribir debug.log con un logger personalizado que no preserva el formato Pierdes el prefijo del namespace y el coloreo. Reutilizá debug.log o envolvé la función sin cambiar el formato.
No poder ver logs porque outputToStdout está redirigido Depurar se vuelve difícil. Asegurate de que process.stdout/stderr estén disponibles; usa debug.log = console.error.bind(console) si es necesario.

6. Resumen y próximos pasos

El módulo debug de Node.js da la capacidad de controlar los registros a nivel de namespace con un mínimo esfuerzo. Al adoptar un enfoque disciplinado:

  • Definir un sistema de naming claro
  • Gestionar la variable DEBUG según el entorno
  • Integrar los logs en cada módulo

Puedes lograr una vista granular sobre el funcionamiento de tu aplicación sin llenar el sistema con ruido.

Próximos pasos:

  • Integra el módulo en un proyecto existente, empezando con un único namespace (myapp:start).
  • Configurá un script dev en package.json que exporte el valor adecuado de DEBUG.
  • Prueba diferentes patrones (myapp:*, myapp:api:*) para ver cómo filtran.
  • Considera envolver debug en un logger estructurado (ej: JSON) si necesitas enviar logs a un sistema externo.

Con estos hábitos, depurar será más rápido, el código será más limpio y todo el equipo sabrá exactamente qué está pasando en cada parte de la pila Node.js.


Felices logs!

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario