Usá el módulo debug de Node.js para logs condicionales por namespace
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:
- Cómo definir y organizar namespaces.
- Cómo activar o desactivar grupos de logs usando variables de entorno.
- Cómo integrar el módulo
debugen código modular y en la línea de comandos. - 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 PostgreSQLSi 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 recibidaCon DEBUG=myapp:api:warning:
myapp:api:warning Recurso no encontrado3. 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álida3.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 deslogueadoSi 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
- Elegí un estilo de namespace consistente. Usá el formato
app:subsystem:action. Esto facilita filtrar con*. - Activa solo lo necesario en producción. Un ejemplo útil:
DEBUG=myapp:apien un entorno de staging, yDEBUG=(vacío) en producción. - 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.
- Agregá un nivel (INFO/ERROR) explícitamente. El módulo no impone niveles, así que añadir
[INFO]ayuda a filtrar visualmente. - Usá colores en la terminal. La biblioteca
debugagrega colores automáticamente si la salida es un tty. No intentes sobrescribirdebug.logcon un wrapper que quite colores a menos que sea necesario. - Documentá los namespaces en
README.md. Incluí un pequeño resumen de qué logs producirá cada namespace. - Versioná los cambios en
DEBUG. Los equipos a menudo usan un.env.examplepara 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
DEBUGsegú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
devenpackage.jsonque exporte el valor adecuado deDEBUG. - Prueba diferentes patrones (
myapp:*,myapp:api:*) para ver cómo filtran. - Considera envolver
debugen 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!
DUGLAS MORENO
Comentarios (0)
Sé el primero en comentar.