Manejo de procesos del sistema en Node.js ES Modules
Node.js proporciona acceso completo a los procesos del sistema operativo a través del módulo process (global) y módulos adicionales como os, child_process, y worker_threads. Estos permiten interactuar con el entorno de ejecución, gestionar procesos hijos y aprovechar múltiples núcleos de CPU.
Módulo Process - Información del Proceso Actual
El objeto process es global en Node.js y no requiere importación. Proporciona información y control sobre el proceso actual de Node.js.
// Función para mostrar información completa sobre el proceso actual de Node.js
// El objeto global 'process' proporciona información y control sobre el proceso actual
function mostrarInfoProceso() {
console.log("=== INFORMACIÓN DEL PROCESO NODE.JS ===");
// Información de identificación del proceso
// process.pid - Process ID: Identificador único asignado por el sistema operativo
console.log("ID del proceso:", process.pid);
// process.ppid - Parent Process ID: ID del proceso que creó este proceso
console.log("ID del proceso padre:", process.ppid);
// process.title - Título del proceso: Nombre que aparece en listas de procesos
console.log("Título del proceso:", process.title);
// Información de ejecución y entorno
// process.version - Versión de Node.js en uso
console.log("Versión de Node.js:", process.version);
// process.versions - Versiones de todas las dependencias internas
console.log("Versiones de dependencias:", process.versions);
// process.platform - Sistema operativo donde se ejecuta
console.log("Plataforma:", process.platform);
// process.arch - Arquitectura del procesador
console.log("Arquitectura:", process.arch);
// Directorios y rutas importantes
// process.cwd() - Current Working Directory: Directorio desde donde se ejecutó el proceso
console.log("Directorio de trabajo actual:", process.cwd());
// process.execPath - Ruta completa al ejecutable de Node.js
console.log("Directorio del ejecutable Node.js:", process.execPath);
// Argumentos de línea de comandos
// process.argv - Array con todos los argumentos pasados al proceso
// process.argv[0]: Ruta al ejecutable de Node.js
// process.argv[1]: Ruta al script que se está ejecutando
// process.argv[2+]: Argumentos adicionales proporcionados por el usuario
console.log("Argumentos recibidos:", process.argv);
console.log("Ruta del script ejecutado:", process.argv[1]);
}
// Ejecutar la función
mostrarInfoProceso();
// EJEMPLOS ADICIONALES Y EXPLICACIONES DETALLADAS:
// EJEMPLO 1: INFORMACIÓN DE MEMORIA Y RENDIMIENTO
function mostrarInfoMemoria() {
console.log("\n=== INFORMACIÓN DE MEMORIA ===");
// process.memoryUsage() - Uso actual de memoria
const memoria = process.memoryUsage();
console.log("Uso de memoria:");
console.log(
" RSS (Resident Set Size):",
(memoria.rss / 1024 / 1024).toFixed(2),
"MB"
);
console.log(
" Heap Total:",
(memoria.heapTotal / 1024 / 1024).toFixed(2),
"MB"
);
console.log(
" Heap Usado:",
(memoria.heapUsed / 1024 / 1024).toFixed(2),
"MB"
);
console.log(" External:", (memoria.external / 1024 / 1024).toFixed(2), "MB");
// process.uptime() - Tiempo que el proceso ha estado ejecutándose en segundos
console.log("Tiempo activo:", process.uptime().toFixed(2), "segundos");
// process.cpuUsage() - Uso de CPU (requiere comparación con medición anterior)
const usoCPU = process.cpuUsage();
console.log(
"Uso de CPU (user):",
(usoCPU.user / 1000000).toFixed(2),
"segundos"
);
console.log(
"Uso de CPU (system):",
(usoCPU.system / 1000000).toFixed(2),
"segundos"
);
}
// EJEMPLO 2: VARIABLES DE ENTORNO
function mostrarVariablesEntorno() {
console.log("\n=== VARIABLES DE ENTORNO ===");
// process.env - Objeto con todas las variables de entorno
console.log("NODE_ENV:", process.env.NODE_ENV || "No definido");
console.log(
"PATH:",
process.env.PATH
? process.env.PATH.split(":").length + " elementos"
: "No disponible"
);
console.log(
"USER:",
process.env.USER || process.env.USERNAME || "Desconocido"
);
console.log(
"HOME:",
process.env.HOME || process.env.USERPROFILE || "No disponible"
);
// Variables de entorno específicas de Node.js
console.log(
"Versión Node.js (desde env):",
process.env.NODE_VERSION || "No definida"
);
}
// EJEMPLO 3: INFORMACIÓN DE EJECUCIÓN Y CARACTERÍSTICAS
function mostrarInfoEjecucion() {
console.log("\n=== INFORMACIÓN DE EJECUCIÓN ===");
// process.release - Información sobre el release de Node.js
console.log("Información del release:", process.release);
// process.config - Configuración de compilación de Node.js
console.log(
"Configuración de compilación disponible:",
Object.keys(process.config).length,
"propiedades"
);
// process.features - Características disponibles en esta compilación
console.log(
"Características disponibles:",
Object.keys(process.features).filter((k) => process.features[k]).length
);
// process.connected - Si el proceso está conectado a un proceso padre (IPC)
console.log("Conectado a proceso padre:", process.connected);
}
// EJEMPLO 4: MANEJO DE SEÑALES DEL SISTEMA
function configurarManejoSenales() {
console.log("\n=== CONFIGURACIÓN DE MANEJO DE SEÑALES ===");
// process.on('SIGINT') - Manejar Ctrl+C en terminal
process.on("SIGINT", () => {
console.log("\nSeñal SIGINT recibida (Ctrl+C). Cerrando gracefulmente...");
process.exit(0);
});
// process.on('SIGTERM') - Manejar señal de terminación (Docker, Kubernetes)
process.on("SIGTERM", () => {
console.log("Señal SIGTERM recibida. Cerrando aplicación...");
process.exit(0);
});
// process.on('exit') - Ejecutar código antes de que el proceso termine
process.on("exit", (code) => {
console.log(`Proceso terminando con código: ${code}`);
});
console.log("Manejo de señales configurado para SIGINT y SIGTERM");
}
// EJEMPLO 5: INFORMACIÓN DE HARDWARE Y LÍMITES
function mostrarLimitesSistema() {
console.log("\n=== LÍMITES DEL SISTEMA ===");
// process.getActiveResourcesInfo() - Recursos activos (Node.js 17+)
if (process.getActiveResourcesInfo) {
const recursos = process.getActiveResourcesInfo();
console.log("Recursos activos:", recursos.length);
console.log("Tipos de recursos:", [...new Set(recursos)].join(", "));
}
// Información sobre las capacidades del sistema
console.log("Número de CPUs:", require("os").cpus().length);
console.log(
"Memoria total:",
(require("os").totalmem() / 1024 / 1024 / 1024).toFixed(2),
"GB"
);
console.log(
"Memoria libre:",
(require("os").freemem() / 1024 / 1024 / 1024).toFixed(2),
"GB"
);
}
// EJEMPLO 6: CLASE PARA MONITOREO DE PROCESO
class MonitorProceso {
constructor() {
this.metricas = {
inicio: new Date(),
usoMemoria: [],
eventos: [],
};
this.iniciarMonitoreo();
}
iniciarMonitoreo() {
// Monitorear memoria cada 5 segundos
this.intervaloMemoria = setInterval(() => {
const memoria = process.memoryUsage();
this.metricas.usoMemoria.push({
timestamp: new Date(),
heapUsed: memoria.heapUsed,
rss: memoria.rss,
});
// Mantener solo las últimas 100 mediciones
if (this.metricas.usoMemoria.length > 100) {
this.metricas.usoMemoria.shift();
}
}, 5000);
// Registrar eventos del proceso
process.on("exit", () => this.registrarEvento("exit"));
process.on("uncaughtException", (error) =>
this.registrarEvento("uncaughtException", error.message)
);
}
registrarEvento(tipo, datos = null) {
this.metricas.eventos.push({
tipo,
timestamp: new Date(),
datos,
});
}
obtenerEstadisticas() {
const ahora = new Date();
const uptime = (ahora - this.metricas.inicio) / 1000;
const memoriaActual = process.memoryUsage();
return {
uptime: uptime.toFixed(2) + " segundos",
memoriaActual: {
heapUsed: (memoriaActual.heapUsed / 1024 / 1024).toFixed(2) + " MB",
rss: (memoriaActual.rss / 1024 / 1024).toFixed(2) + " MB",
},
totalEventos: this.metricas.eventos.length,
metricasMemoria: this.metricas.usoMemoria.length,
};
}
detenerMonitoreo() {
if (this.intervaloMemoria) {
clearInterval(this.intervaloMemoria);
}
}
}
// EJEMPLO 7: MANIPULACIÓN DEL PROCESO
function demostrarManipulacionProceso() {
console.log("\n=== DEMOSTRACIÓN DE MANIPULACIÓN ===");
// process.chdir() - Cambiar directorio de trabajo
const directorioOriginal = process.cwd();
console.log("Directorio original:", directorioOriginal);
// process.exit() - Terminar el proceso con código de salida
// process.exit(0) - Éxito
// process.exit(1) - Error
console.log("Para salir ejecuta: process.exit(0)");
// process.nextTick() - Ejecutar código en el próximo tick del event loop
process.nextTick(() => {
console.log("Esto se ejecuta en el próximo tick del event loop");
});
}
// EJEMPLO 8: INFORMACIÓN DE DEPURACIÓN
function mostrarInfoDepuracion() {
console.log("\n=== INFORMACIÓN PARA DEPURACIÓN ===");
// process.debugPort - Puerto usado para debugging
console.log("Puerto de depuración:", process.debugPort);
// process.execArgv - Argumentos pasados al ejecutable Node.js
console.log("Argumentos del ejecutable Node.js:", process.execArgv);
// process.env.NODE_OPTIONS - Opciones de Node.js
console.log(
"Opciones de Node.js:",
process.env.NODE_OPTIONS || "No definidas"
);
// Información sobre el event loop
console.log("Tiempo en event loop:", process.uptime(), "segundos");
}
// EJEMPLO 9: GESTIÓN DE DEPENDENCIAS Y MÓDULOS
function mostrarInfoModulos() {
console.log("\n=== INFORMACIÓN DE MÓDULOS ===");
// process.mainModule - Módulo principal que inició la aplicación
if (process.mainModule) {
console.log("Módulo principal:", process.mainModule.filename);
console.log("Rutas de búsqueda:", process.mainModule.paths.slice(0, 3));
}
// require.main - Alternativa para obtener el módulo principal
if (require.main) {
console.log("Archivo principal:", require.main.filename);
}
// process.moduleLoadList - Lista de módulos cargados (solo en algunas versiones)
if (process.moduleLoadList) {
console.log(
"Módulos cargados (~10 primeros):",
process.moduleLoadList.slice(0, 10)
);
}
}
// EJEMPLO 10: FUNCIÓN COMPLETA DE DIAGNÓSTICO
function diagnosticoCompleto() {
console.log("=== DIAGNÓSTICO COMPLETO DEL PROCESO ===\n");
// Información básica
mostrarInfoProceso();
// Memoria y rendimiento
mostrarInfoMemoria();
// Variables de entorno
mostrarVariablesEntorno();
// Información de ejecución
mostrarInfoEjecucion();
// Información de hardware
mostrarLimitesSistema();
// Información para depuración
mostrarInfoDepuracion();
// Información de módulos
mostrarInfoModulos();
// Configurar manejo de señales
configurarManejoSenales();
// Demostrar manipulación
demostrarManipulacionProceso();
console.log("\n=== DIAGNÓSTICO COMPLETADO ===");
}
// EJEMPLO 11: USO DEL MONITOR DE PROCESO
async function usarMonitorProceso() {
const monitor = new MonitorProceso();
console.log("\n=== MONITOREO EN TIEMPO REAL ===");
console.log("Iniciando monitoreo por 10 segundos...");
// Esperar 10 segundos mostrando estadísticas
for (let i = 0; i < 10; i++) {
await new Promise((resolve) => setTimeout(resolve, 1000));
const stats = monitor.obtenerEstadisticas();
console.log(
`[${i + 1}s] ${stats.uptime} - Memoria: ${stats.memoriaActual.heapUsed}`
);
}
console.log("\nEstadísticas finales:");
console.log(monitor.obtenerEstadisticas());
monitor.detenerMonitoreo();
}
// EJECUCIÓN DE EJEMPLOS
async function ejecutarEjemplos() {
console.log("=== EJEMPLOS DE INFORMACIÓN DEL PROCESO NODE.JS ===\n");
// Ejemplo básico
mostrarInfoProceso();
// Ejemplos adicionales
mostrarInfoMemoria();
mostrarVariablesEntorno();
mostrarInfoEjecucion();
configurarManejoSenales();
mostrarLimitesSistema();
// Usar el monitor
await usarMonitorProceso();
// Diagnóstico completo
diagnosticoCompleto();
console.log("\n=== TODOS LOS EJEMPLOS COMPLETADOS ===");
}
// Descomentar para ejecutar todos los ejemplos
// ejecutarEjemplos();
// Ejecutar solo la función básica por defecto
mostrarInfoProceso();
function mostrarInfoProceso() {
console.log("=== INFORMACIÓN DEL PROCESO NODE.JS ===");
// Información de identificación
console.log("ID del proceso:", process.pid);
console.log("ID del proceso padre:", process.ppid);
console.log("Título del proceso:", process.title);
// Información de ejecución
console.log("Versión de Node.js:", process.version);
console.log("Versiones de dependencias:", process.versions);
console.log("Plataforma:", process.platform);
console.log("Arquitectura:", process.arch);
// Directorios
console.log("Directorio de trabajo actual:", process.cwd());
console.log("Directorio del ejecutable Node.js:", process.execPath);
// Argumentos de línea de comandos
console.log("Argumentos recibidos:", process.argv);
console.log("Ruta del script ejecutado:", process.argv[1]);
}
mostrarInfoProceso();
Propiedades principales del objeto process.env
- Identificación:
pid,ppid,title - Entorno:
version,platform,arch,env - Rutas:
cwd(),execPath,argv - Rendimiento:
memoryUsage(),cpuUsage(),uptime() - Configuración:
versions,release,config
Eventos importantes del proceso
SIGINT: Interrupción (Ctrl+C)SIGTERM: Terminación gracefulexit: Antes de que el proceso termineuncaughtException: Excepciones no capturadas
Casos de uso reales
- Configuración: Adaptar comportamiento según entorno
- Monitoring: Seguimiento de rendimiento y recursos
- Debugging: Diagnóstico de problemas
- Logging: Información contextual en logs
- Signal handling: Cierre graceful de aplicaciones
Beneficios
- Información en tiempo real del proceso
- Control sobre el ciclo de vida de la aplicación
- Adaptabilidad a diferentes entornos
- Herramientas para diagnóstico y troubleshooting
Consideraciones de seguridad
Nunca confíes ciegamente en lo que el usuario introduce en la línea de comandos. Valida siempre los tipos y contenidos. Evita ejecutar comandos del sistema formados a partir de valores que provengan de process.argv.
Interfaces interactivas con readline
El módulo readline facilita crear programas interactivos que reciben datos del usuario línea por línea. Es fundamental para construir asistentes, generadores de proyectos y herramientas de consola profesionales.
A diferencia del uso directo de stdin, readline proporciona utilidades más avanzadas, como historial, autocompletado o manejo de eventos.
Ejemplo básico creando una interfaz que hace preguntas consecutivas.
// archivo: readline_basico.js
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
async function main() {
// creamos la interfaz mediante la versión con promesas de readline
const rl = readline.createInterface({ input, output });
// preguntamos de forma secuencial
const nombre = await rl.question("¿cómo te llamas? ");
const lenguaje = await rl.question("¿cuál es tu lenguaje favorito? ");
console.log(`hola ${nombre}, veo que te gusta ${lenguaje}`);
// cerramos la interfaz para liberar recursos
rl.close();
}
await main();
Ejemplo más complejo con un menú interactivo persistente.
// archivo: readline_menu.js
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
async function main() {
const rl = readline.createInterface({ input, output });
async function mostrarMenu() {
console.log("1. decir hola");
console.log("2. mostrar hora actual");
console.log("3. salir");
const opcion = await rl.question("elige una opción: ");
return opcion.trim();
}
let continuar = true;
while (continuar) {
const opcion = await mostrarMenu();
if (opcion === "1") {
console.log("hola");
} else if (opcion === "2") {
console.log(`hora actual: ${new Date().toLocaleTimeString()}`);
} else if (opcion === "3") {
continuar = false;
} else {
console.log("opción no válida");
}
}
rl.close();
}
await main();
Casos de uso reales
readline es la base de muchas herramientas CLI, como generadores de proyectos de frontend, asistentes de configuración, exploradores de bases de datos o scripts de administración.
Consideraciones de seguridad
Controla siempre la entrada. En aplicaciones sensibles, evita aceptar comandos arbitrarios escritos por el usuario.
Herramienta completa CLI
// archivo: cli_completa.js
import readline from "node:readline/promises";
import { spawn } from "node:child_process";
import { stdin as input, stdout as output } from "node:process";
async function ejecutarComando(comando, args = []) {
return new Promise((resolve) => {
const proc = spawn(comando, args);
proc.stdout.on("data", (d) => console.log(d.toString()));
proc.stderr.on("data", (d) => console.error(d.toString()));
proc.on("close", (codigo) => resolve(codigo));
});
}
async function main() {
const rl = readline.createInterface({ input, output });
process.on("SIGTERM", async () => {
console.log("SIGTERM recibido: cerrando CLI");
rl.close();
process.exit(0);
});
async function menu() {
console.log("1. mostrar directorio actual");
console.log("2. mostrar variables de entorno");
console.log("3. ejecutar comando del sistema");
console.log("4. salir");
return rl.question("elige una opción: ");
}
let activo = true;
while (activo) {
const opcion = (await menu()).trim();
if (opcion === "1") {
console.log(`cwd: ${process.cwd()}`);
} else if (opcion === "2") {
console.log(process.env);
} else if (opcion === "3") {
const cmd = await rl.question("introduce comando: ");
const partes = cmd.split(" ");
const comando = partes[0];
const args = partes.slice(1);
await ejecutarComando(comando, args);
} else if (opcion === "4") {
activo = false;
} else {
console.log("opción no válida");
}
}
rl.close();
}
await main();
Variables de Entorno (process.env)
Las variables de entorno se utilizan para guardar configuraciones sensibles o cambiantes, como claves de API, puertos del servidor o rutas especiales. Son valores que provienen del sistema operativo o de un archivo de configuración externo, no del código fuente.
Node las expone mediante el objeto process.env, donde cada variable se accede como una propiedad. Todas las variables de entorno se almacenan como cadenas, por lo que si necesitas números o booleanos debes convertirlos.
// Función para demostrar el manejo de variables de entorno en Node.js
// process.env es un objeto que contiene todas las variables de entorno del sistema
function manejarVariablesEntorno() {
console.log("=== VARIABLES DE ENTORNO ===");
// Acceder a variables de entorno específicas
// process.env.NODE_ENV - Variable crucial que define el entorno de ejecución
// Valores comunes: 'development', 'production', 'test'
// Si no está definida, usamos 'no definido' como valor por defecto
console.log(
"Entorno de ejecución (NODE_ENV):",
process.env.NODE_ENV || "no definido"
);
// process.env.USER / process.env.USERNAME - Nombre del usuario del sistema
// USER es común en Unix/Linux, USERNAME en Windows
console.log(
"Usuario del sistema:",
process.env.USER || process.env.USERNAME || "no disponible"
);
// process.env.HOME / process.env.USERPROFILE - Directorio home del usuario
// HOME en Unix/Linux, USERPROFILE en Windows
console.log(
"Directorio home:",
process.env.HOME || process.env.USERPROFILE || "no disponible"
);
// process.env.PATH - Rutas de búsqueda de ejecutables del sistema
console.log(
"Path del sistema:",
process.env.PATH
? "Disponible (" + process.env.PATH.split(":").length + " elementos)"
: "no disponible"
);
// Establecer variables de entorno (solo para este proceso)
// Las variables establecidas de esta manera solo existen durante la vida del proceso
// No afectan al sistema operativo ni a otros procesos
process.env.MI_VARIABLE = "valor personalizado";
console.log("Variable personalizada:", process.env.MI_VARIABLE);
// Listar todas las variables de entorno
// Object.keys(process.env) retorna un array con todas las claves disponibles
console.log(
"Total de variables de entorno:",
Object.keys(process.env).length
);
}
// Ejecutar la función
manejarVariablesEntorno();
// EJEMPLOS ADICIONALES Y EXPLICACIONES DETALLADAS:
// EJEMPLO 1: VARIABLES DE ENTORNO COMUNES EN DESARROLLO
function mostrarVariablesDesarrollo() {
console.log("\n=== VARIABLES DE DESARROLLO COMUNES ===");
// Variables comunes en entornos de desarrollo
console.log("PUERTO (PORT):", process.env.PORT || "3000 (por defecto)");
console.log("HOST:", process.env.HOST || "localhost (por defecto)");
console.log(
"BASE DE DATOS (DATABASE_URL):",
process.env.DATABASE_URL ? "Definida" : "No definida"
);
console.log(
"API KEY:",
process.env.API_KEY ? "***" + process.env.API_KEY.slice(-4) : "No definida"
);
// Variables de debugging
console.log("DEBUG:", process.env.DEBUG || "No habilitado");
console.log("LOG_LEVEL:", process.env.LOG_LEVEL || "info (por defecto)");
}
// EJEMPLO 2: CONFIGURACIÓN DE APLICACIÓN CON VARIABLES DE ENTORNO
class ConfiguracionApp {
constructor() {
this.cargarConfiguracion();
}
cargarConfiguracion() {
this.config = {
// Entorno
entorno: process.env.NODE_ENV || "development",
// Servidor
puerto: parseInt(process.env.PORT) || 3000,
host: process.env.HOST || "localhost",
// Base de datos
database: {
url: process.env.DATABASE_URL,
maxConnections: parseInt(process.env.DB_MAX_CONNECTIONS) || 10,
timeout: parseInt(process.env.DB_TIMEOUT) || 30000,
},
// API y servicios externos
api: {
clave: process.env.API_KEY,
urlBase: process.env.API_BASE_URL || "https://api.ejemplo.com",
timeout: parseInt(process.env.API_TIMEOUT) || 5000,
},
// Seguridad
seguridad: {
jwtSecreto: process.env.JWT_SECRETO || "secreto-desarrollo",
bcryptRondas: parseInt(process.env.BCRYPT_RONDAS) || 10,
corsOrigen: process.env.CORS_ORIGEN || "*",
},
// Logging
logging: {
nivel: process.env.LOG_LEVEL || "info",
archivo: process.env.LOG_FILE || "app.log",
},
};
}
validarConfiguracion() {
const errores = [];
// Validaciones requeridas para producción
if (this.config.entorno === "production") {
if (!process.env.DATABASE_URL) {
errores.push("DATABASE_URL es requerida en producción");
}
if (
!process.env.JWT_SECRETO ||
this.config.seguridad.jwtSecreto === "secreto-desarrollo"
) {
errores.push("JWT_SECRETO seguro es requerido en producción");
}
if (!process.env.API_KEY) {
errores.push("API_KEY es requerida en producción");
}
}
return errores;
}
mostrarConfiguracion() {
console.log("\n=== CONFIGURACIÓN DE LA APLICACIÓN ===");
console.log("Entorno:", this.config.entorno);
console.log("Servidor:", `${this.config.host}:${this.config.puerto}`);
console.log(
"Base de datos:",
this.config.database.url ? "Configurada" : "No configurada"
);
console.log("API Externa:", this.config.api.urlBase);
console.log("Log Level:", this.config.logging.nivel);
const errores = this.validarConfiguracion();
if (errores.length > 0) {
console.log("\nADVERTENCIAS DE CONFIGURACIÓN:");
errores.forEach((error) => console.log(" -", error));
}
}
}
// EJEMPLO 3: MANEJO SEGURO DE VARIABLES SENSIBLES
function manejoSeguroVariables() {
console.log("\n=== MANEJO SEGURO DE VARIABLES SENSIBLES ===");
// Nunca mostrar variables sensibles directamente
const variablesSensibles = [
"API_KEY",
"JWT_SECRET",
"DATABASE_PASSWORD",
"AWS_SECRET_ACCESS_KEY",
"STRIPE_SECRET_KEY",
];
variablesSensibles.forEach((variable) => {
if (process.env[variable]) {
const valor = process.env[variable];
const valorOculto = valor.length > 8 ? "***" + valor.slice(-4) : "***";
console.log(`${variable}: ${valorOculto}`);
} else {
console.log(`${variable}: No definida`);
}
});
// Verificar que las variables requeridas existen
const variablesRequeridas = ["DATABASE_URL", "NODE_ENV"];
const faltantes = variablesRequeridas.filter((v) => !process.env[v]);
if (faltantes.length > 0) {
console.log("\nVARIABLES REQUERIDAS FALTANTES:", faltantes.join(", "));
} else {
console.log("\nTodas las variables requeridas están definidas");
}
}
// EJEMPLO 4: CARGAR VARIABLES DESDE ARCHIVO .env
function demostrarDotEnv() {
console.log("\n=== USO DE ARCHIVOS .env ===");
// En una aplicación real, usarías: require('dotenv').config();
console.log("Para usar archivos .env, instala el paquete dotenv:");
console.log("npm install dotenv");
console.log("\nY en tu código:");
console.log("require('dotenv').config();");
console.log("// Esto carga las variables desde .env a process.env");
// Ejemplo de estructura de archivo .env
console.log("\nEjemplo de archivo .env:");
console.log("NODE_ENV=development");
console.log("PORT=3000");
console.log(
"DATABASE_URL=postgresql://usuario:contraseña@localhost:5432/midb"
);
console.log("API_KEY=tu_clave_secreta_aqui");
console.log("JWT_SECRET=mi_secreto_super_seguro");
}
// EJEMPLO 5: VARIABLES POR DEFECTO Y VALIDACIÓN
function configuracionConDefaults() {
console.log("\n=== CONFIGURACIÓN CON VALORES POR DEFECTO ===");
// Patrón común: variable de entorno o valor por defecto
const config = {
// Con operador OR para valores por defecto simples
puerto: process.env.PORT || 3000,
entorno: process.env.NODE_ENV || "development",
// Con función para lógica más compleja
logLevel: (() => {
const nivel = process.env.LOG_LEVEL;
const nivelesValidos = ["error", "warn", "info", "debug"];
return nivelesValidos.includes(nivel) ? nivel : "info";
})(),
// Con conversión de tipos
timeouts: {
baseDatos: parseInt(process.env.DB_TIMEOUT) || 30000,
api: parseInt(process.env.API_TIMEOUT) || 5000,
reconnect: parseInt(process.env.RECONNECT_TIMEOUT) || 1000,
},
// Con validación de existencia
baseDatos: process.env.DATABASE_URL
? { url: process.env.DATABASE_URL }
: null,
};
console.log("Configuración cargada:");
console.log("- Puerto:", config.puerto);
console.log("- Entorno:", config.entorno);
console.log("- Log Level:", config.logLevel);
console.log("- Timeouts:", config.timeouts);
console.log(
"- Base de datos:",
config.baseDatos ? "Configurada" : "No configurada"
);
}
// EJEMPLO 6: VARIABLES DE ENTORNO ESPECÍFICAS DE NODE.JS
function variablesEspecificasNode() {
console.log("\n=== VARIABLES ESPECÍFICAS DE NODE.JS ===");
// Variables que afectan el comportamiento de Node.js
const variablesNode = {
NODE_PATH: "Rutas de búsqueda de módulos",
NODE_OPTIONS: "Opciones de línea de comandos",
NODE_ENV: "Entorno de ejecución",
NODE_DEBUG: "Módulos para debugging",
NODE_DISABLE_COLORS: "Deshabilitar colores en consola",
NODE_NO_WARNINGS: "Suprimir advertencias",
};
console.log("Variables específicas de Node.js:");
Object.entries(variablesNode).forEach(([variable, descripcion]) => {
const valor = process.env[variable];
console.log(
` ${variable}: ${valor ? valor : "No definida"} (${descripcion})`
);
});
}
// EJEMPLO 7: GESTIÓN DE CONFIGURACIÓN POR ENTORNO
class GestorConfiguracion {
constructor() {
this.entorno = process.env.NODE_ENV || "development";
this.config = this.cargarConfiguracionPorEntorno();
}
cargarConfiguracionPorEntorno() {
const configBase = {
// Configuración base común a todos los entornos
app: {
nombre: process.env.APP_NAME || "Mi Aplicación",
version: process.env.APP_VERSION || "1.0.0",
},
caracteristicas: {
cache: process.env.ENABLE_CACHE !== "false",
compresion: process.env.ENABLE_COMPRESSION !== "false",
},
};
// Configuración específica por entorno
const configPorEntorno = {
development: {
debug: true,
baseDatos: {
url: process.env.DATABASE_URL || "postgresql://localhost:5432/dev",
logging: true,
},
},
test: {
debug: false,
baseDatos: {
url: process.env.DATABASE_URL || "postgresql://localhost:5432/test",
logging: false,
},
},
production: {
debug: false,
baseDatos: {
url: process.env.DATABASE_URL, // Requerida en producción
logging: false,
ssl: true,
},
},
};
return {
...configBase,
...configPorEntorno[this.entorno],
};
}
obtener(variable, valorPorDefecto = null) {
// Buscar en variables de entorno primero
if (process.env[variable] !== undefined) {
return process.env[variable];
}
// Buscar en configuración cargada
const valor = variable
.split(".")
.reduce((obj, key) => obj && obj[key], this.config);
return valor !== undefined ? valor : valorPorDefecto;
}
mostrarResumen() {
console.log("\n=== RESUMEN DE CONFIGURACIÓN ===");
console.log("Entorno:", this.entorno);
console.log("Nombre App:", this.config.app.nombre);
console.log("Debug:", this.config.debug);
console.log("Cache:", this.config.caracteristicas.cache);
console.log(
"Base de datos:",
this.config.baseDatos.url ? "Configurada" : "No configurada"
);
}
}
// EJEMPLO 8: VALIDACIÓN Y SANITIZACIÓN DE VARIABLES
function validarYSanitizarVariables() {
console.log("\n=== VALIDACIÓN Y SANITIZACIÓN ===");
const validaciones = [
{
variable: "PORT",
requerida: false,
tipo: "numero",
min: 1,
max: 65535,
valorPorDefecto: 3000,
},
{
variable: "NODE_ENV",
requerida: false,
tipo: "string",
valoresPermitidos: ["development", "production", "test"],
valorPorDefecto: "development",
},
{
variable: "DATABASE_URL",
requerida: process.env.NODE_ENV === "production",
tipo: "string",
patron: /^postgresql:\/\//,
mensajeError: "Debe ser una URL de PostgreSQL válida",
},
{
variable: "API_TIMEOUT",
requerida: false,
tipo: "numero",
min: 1000,
max: 30000,
valorPorDefecto: 5000,
},
];
const resultados = [];
validaciones.forEach((validacion) => {
let valor = process.env[validacion.variable];
let valido = true;
let mensaje = "OK";
// Si no existe y es requerida
if (!valor && validacion.requerida) {
valido = false;
mensaje = "Variable requerida no definida";
}
// Si no existe pero no es requerida, usar valor por defecto
else if (!valor && validacion.valorPorDefecto !== undefined) {
valor = validacion.valorPorDefecto;
mensaje = `Usando valor por defecto: ${valor}`;
}
// Validar tipo y formato
else if (valor) {
if (validacion.tipo === "numero") {
const numero = parseInt(valor);
if (isNaN(numero)) {
valido = false;
mensaje = "Debe ser un número válido";
} else if (validacion.min !== undefined && numero < validacion.min) {
valido = false;
mensaje = `Debe ser mayor o igual a ${validacion.min}`;
} else if (validacion.max !== undefined && numero > validacion.max) {
valido = false;
mensaje = `Debe ser menor o igual a ${validacion.max}`;
}
}
if (
validacion.valoresPermitidos &&
!validacion.valoresPermitidos.includes(valor)
) {
valido = false;
mensaje = `Valores permitidos: ${validacion.valoresPermitidos.join(
", "
)}`;
}
if (validacion.patron && !validacion.patron.test(valor)) {
valido = false;
mensaje = validacion.mensajeError || "No cumple el formato requerido";
}
}
resultados.push({
variable: validacion.variable,
valor: valor || "No definido",
valido,
mensaje,
});
});
console.log("Resultados de validación:");
resultados.forEach((resultado) => {
const icono = resultado.valido ? "ok" : "error";
console.log(
`${icono} ${resultado.variable}: ${resultado.valor} - ${resultado.mensaje}`
);
});
}
// EJEMPLO 9: USO PRÁCTICO EN APLICACIÓN WEB
function demostracionAplicacionWeb() {
console.log("\n=== CONFIGURACIÓN PARA APLICACIÓN WEB ===");
// Configuración típica de una aplicación web
const configWeb = {
servidor: {
puerto: process.env.PORT || 3000,
host: process.env.HOST || "0.0.0.0",
entorno: process.env.NODE_ENV || "development",
},
baseDatos: {
cliente: "pg",
connection: process.env.DATABASE_URL || {
host: process.env.DB_HOST || "127.0.0.1",
port: process.env.DB_PORT || 5432,
user: process.env.DB_USER || "postgres",
password: process.env.DB_PASSWORD || "password",
database: process.env.DB_NAME || "mi_app_dev",
},
pool: {
min: parseInt(process.env.DB_POOL_MIN) || 2,
max: parseInt(process.env.DB_POOL_MAX) || 10,
},
},
autenticacion: {
jwtSecreto:
process.env.JWT_SECRET ||
"secreto-desarrollo-solo-cambiar-en-produccion",
expiracion: process.env.JWT_EXPIRATION || "7d",
},
serviciosExternos: {
email: {
apiKey: process.env.SENDGRID_API_KEY,
from: process.env.EMAIL_FROM || "noreply@miapp.com",
},
almacenamiento: {
bucket: process.env.AWS_BUCKET_NAME,
region: process.env.AWS_REGION || "us-east-1",
},
},
};
console.log("Configuración cargada para aplicación web:");
console.log(
"- Servidor:",
`${configWeb.servidor.host}:${configWeb.servidor.puerto}`
);
console.log("- Entorno:", configWeb.servidor.entorno);
console.log(
"- Base de datos:",
configWeb.baseDatos.connection ? "Configurada" : "No configurada"
);
console.log(
"- Autenticación JWT:",
configWeb.autenticacion.jwtSecreto ? "Configurada" : "No configurada"
);
}
// EJECUCIÓN DE EJEMPLOS
function ejecutarEjemplos() {
console.log("=== EJEMPLOS DE VARIABLES DE ENTORNO EN NODE.JS ===\n");
// Ejemplo básico
manejarVariablesEntorno();
// Ejemplos adicionales
mostrarVariablesDesarrollo();
// Configuración de aplicación
const configApp = new ConfiguracionApp();
configApp.mostrarConfiguracion();
// Manejo seguro
manejoSeguroVariables();
// Dotenv
demostrarDotEnv();
// Configuración con defaults
configuracionConDefaults();
// Variables específicas de Node.js
variablesEspecificasNode();
// Gestor de configuración
const gestorConfig = new GestorConfiguracion();
gestorConfig.mostrarResumen();
// Validación
validarYSanitizarVariables();
// Aplicación web
demostracionAplicacionWeb();
console.log("\n=== TODOS LOS EJEMPLOS COMPLETADOS ===");
}
// Descomentar para ejecutar todos los ejemplos
// ejecutarEjemplos();
Características principales
process.env: Objeto que contiene todas las variables de entorno- Acceso seguro: Usar operador
||para valores por defecto - Variables sensibles: Nunca mostrarlas en logs o respuestas
- Validación: Verificar tipos, formatos y valores permitidos
Variables de entorno comunes
NODE_ENV: Entorno de ejecución (development, production, test)PORT: Puerto del servidorDATABASE_URL: URL de conexión a base de datosAPI_KEY: Claves para servicios externosJWT_SECRET: Secreto para tokens JWT
Mejores prácticas
- Usar valores por defecto para desarrollo
- Validar variables en producción
- Nunca commitear archivos .env
- Usar nombres descriptivos en mayúsculas
- Documentar variables requeridas
Consideraciones de seguridad
Nunca subas tus archivos con variables de entorno al repositorio si contienen información sensible. Las claves privadas, tokens o configuraciones críticas deben mantenerse fuera del código. Además, valida siempre los valores antes de usarlos.
- Ocultar variables sensibles en logs
- Validar formatos de URLs y conexiones
- Usar diferentes valores por entorno
- Considerar usar vaults de secrets en producción
Uso de Memoria y Rendimiento
// Función para monitorear el uso de memoria del proceso actual de Node.js
// process.memoryUsage() proporciona información detallada sobre el uso de memoria
function monitorearMemoria() {
console.log("=== USO DE MEMORIA DEL PROCESO ===");
// process.memoryUsage() retorna un objeto con las métricas de memoria actuales
const usoMemoria = process.memoryUsage();
// RSS (Resident Set Size) - Memoria física total asignada al proceso
// Incluye: Código, stack, heap, y memoria de librerías compartidas
console.log(
"RSS (Resident Set Size):",
Math.round(usoMemoria.rss / 1024 / 1024) + " MB"
);
// Heap Total - Tamaño total del heap de V8 (motor JavaScript de Node.js)
// El heap es donde se almacenan los objetos y variables de JavaScript
console.log(
"Heap Total:",
Math.round(usoMemoria.heapTotal / 1024 / 1024) + " MB"
);
// Heap Usado - Parte del heap que está actualmente en uso
// Esta métrica es crucial para detectar memory leaks
console.log(
"Heap Usado:",
Math.round(usoMemoria.heapUsed / 1024 / 1024) + " MB"
);
// External - Memoria usada por objetos C++ vinculados a objetos JavaScript
// Ejemplos: Buffers, sockets, handles de archivos
console.log(
"External:",
Math.round(usoMemoria.external / 1024 / 1024) + " MB"
);
// Uso de CPU - process.cpuUsage() retorna el tiempo de CPU usado
// Los valores están en microsegundos (millonésimas de segundo)
const usoCPU = process.cpuUsage();
console.log("Uso de CPU (user):", usoCPU.user + " microseconds");
console.log("Uso de CPU (system):", usoCPU.system + " microseconds");
}
// Función para monitoreo continuo de memoria
// Útil para detectar memory leaks y patrones de uso de memoria
function iniciarMonitoreoContinuo() {
// Monitorear memoria cada 5 segundos usando setInterval
const intervalo = setInterval(() => {
const heapUsado = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
console.log(`Heap usado: ${heapUsado} MB`);
}, 5000);
// Retornar el intervalo para poder limpiarlo posteriormente
return intervalo;
}
// Ejecutar monitoreo una vez
monitorearMemoria();
// Descomentar para monitoreo continuo
// iniciarMonitoreoContinuo();
// EJEMPLOS ADICIONALES Y EXPLICACIONES DETALLADAS:
// EJEMPLO 1: MONITOREO AVANZADO CON MÁS MÉTRICAS
function monitoreoAvanzado() {
console.log("\n=== MONITOREO AVANZADO DE MEMORIA ===");
const memoria = process.memoryUsage();
const usoCPU = process.cpuUsage();
// Métricas detalladas en diferentes unidades
console.log("Métricas detalladas:");
console.log("RSS:", memoria.rss, "bytes");
console.log("RSS:", (memoria.rss / 1024).toFixed(2), "KB");
console.log("RSS:", (memoria.rss / 1024 / 1024).toFixed(2), "MB");
console.log("RSS:", (memoria.rss / 1024 / 1024 / 1024).toFixed(4), "GB");
// Porcentajes de uso
const porcentajeHeapUsado = (
(memoria.heapUsed / memoria.heapTotal) *
100
).toFixed(1);
console.log(`Heap usado: ${porcentajeHeapUsado}% del total`);
// Uso de CPU en segundos (más legible)
console.log("CPU User:", (usoCPU.user / 1000000).toFixed(3), "segundos");
console.log("CPU System:", (usoCPU.system / 1000000).toFixed(3), "segundos");
console.log(
"CPU Total:",
((usoCPU.user + usoCPU.system) / 1000000).toFixed(3),
"segundos"
);
}
// EJEMPLO 2: DETECTOR DE MEMORY LEAKS
class DetectorMemoryLeaks {
constructor(intervalo = 5000) {
this.intervalo = intervalo;
this.mediciones = [];
this.intervalId = null;
this.umbralCrecimiento = 1.1; // 10% de crecimiento
}
iniciar() {
console.log("Iniciando detector de memory leaks...");
this.intervalId = setInterval(() => {
this.registrarMedicion();
this.verificarLeaks();
}, this.intervalo);
}
detener() {
if (this.intervalId) {
clearInterval(this.intervalId);
console.log("Detector de memory leaks detenido");
}
}
registrarMedicion() {
const memoria = process.memoryUsage();
const medicion = {
timestamp: new Date(),
heapUsed: memoria.heapUsed,
heapTotal: memoria.heapTotal,
rss: memoria.rss,
external: memoria.external,
};
this.mediciones.push(medicion);
// Mantener solo las últimas 100 mediciones
if (this.mediciones.length > 100) {
this.mediciones.shift();
}
}
verificarLeaks() {
if (this.mediciones.length < 3) return;
const primera = this.mediciones[0].heapUsed;
const ultima = this.mediciones[this.mediciones.length - 1].heapUsed;
const crecimiento = ultima / primera;
if (crecimiento > this.umbralCrecimiento) {
console.warn(`POSIBLE MEMORY LEAK DETECTADO:`);
console.warn(`Crecimiento: ${(crecimiento * 100).toFixed(1)}%`);
console.warn(`Inicio: ${Math.round(primera / 1024 / 1024)} MB`);
console.warn(`Actual: ${Math.round(ultima / 1024 / 1024)} MB`);
console.warn(`Período: ${this.mediciones.length} mediciones`);
}
}
obtenerEstadisticas() {
if (this.mediciones.length === 0) return null;
const heapUsados = this.mediciones.map((m) => m.heapUsed);
const min = Math.min(...heapUsados);
const max = Math.max(...heapUsados);
const avg = heapUsados.reduce((a, b) => a + b, 0) / heapUsados.length;
return {
mediciones: this.mediciones.length,
heapMin: Math.round(min / 1024 / 1024) + " MB",
heapMax: Math.round(max / 1024 / 1024) + " MB",
heapAvg: Math.round(avg / 1024 / 1024) + " MB",
periodo: this.mediciones.length * (this.intervalo / 1000) + " segundos",
};
}
}
// EJEMPLO 3: MONITOREO CON LÍMITES Y ALERTAS
function monitoreoConLimites() {
console.log("\n=== MONITOREO CON LÍMITES CONFIGURABLES ===");
const limites = {
heapMaxMB: 500, // Límite máximo de heap usado
rssMaxMB: 1000, // Límite máximo de RSS
crecimientoRapidoMB: 50, // Crecimiento rápido en 5 segundos
};
let medicionAnterior = process.memoryUsage();
const monitor = setInterval(() => {
const memoriaActual = process.memoryUsage();
const heapMB = Math.round(memoriaActual.heapUsed / 1024 / 1024);
const rssMB = Math.round(memoriaActual.rss / 1024 / 1024);
// Verificar límites absolutos
if (heapMB > limites.heapMaxMB) {
console.error(
`ALERTA: Heap usado (${heapMB} MB) excede límite (${limites.heapMaxMB} MB)`
);
}
if (rssMB > limites.rssMaxMB) {
console.error(
`ALERTA: RSS (${rssMB} MB) excede límite (${limites.rssMaxMB} MB)`
);
}
// Verificar crecimiento rápido
const crecimiento = memoriaActual.heapUsed - medicionAnterior.heapUsed;
const crecimientoMB = Math.round(crecimiento / 1024 / 1024);
if (crecimientoMB > limites.crecimientoRapidoMB) {
console.warn(`Crecimiento rápido: +${crecimientoMB} MB en 5 segundos`);
}
medicionAnterior = memoriaActual;
// Mostrar estado actual
console.log(`Estado: Heap=${heapMB}MB, RSS=${rssMB}MB`);
}, 5000);
return monitor;
}
// EJEMPLO 4: BENCHMARK DE USO DE MEMORIA
function benchmarkMemoria(iteraciones = 100000) {
console.log("\n=== BENCHMARK DE USO DE MEMORIA ===");
const memoriaInicial = process.memoryUsage();
// Crear muchos objetos para ver el impacto en memoria
const objetos = [];
for (let i = 0; i < iteraciones; i++) {
objetos.push({
id: i,
data: "x".repeat(100), // String de 100 caracteres
timestamp: new Date(),
metadata: {
index: i,
random: Math.random(),
},
});
}
const memoriaDurante = process.memoryUsage();
// Limpiar los objetos
objetos.length = 0;
// Forzar garbage collection si está disponible
if (global.gc) {
global.gc();
}
const memoriaFinal = process.memoryUsage();
// Calcular diferencias
const diffHeap = memoriaDurante.heapUsed - memoriaInicial.heapUsed;
const diffRSS = memoriaDurante.rss - memoriaInicial.rss;
console.log(`Benchmark completado (${iteraciones} objetos):`);
console.log(`- Heap usado durante: ${Math.round(diffHeap / 1024 / 1024)} MB`);
console.log(`- RSS durante: ${Math.round(diffRSS / 1024 / 1024)} MB`);
console.log(
`- Heap final: ${Math.round(memoriaFinal.heapUsed / 1024 / 1024)} MB`
);
console.log(
`- Memoria recuperada: ${Math.round(
(diffHeap - (memoriaFinal.heapUsed - memoriaInicial.heapUsed)) /
1024 /
1024
)} MB`
);
}
// EJEMPLO 5: MONITOREO DE CPU CON CÁLCULO DE PORCENTAJE
class MonitorCPU {
constructor() {
this.ultimaMedicion = process.cpuUsage();
this.ultimoTimestamp = Date.now();
this.estadisticas = [];
}
medir() {
const ahora = Date.now();
const tiempoTranscurrido = (ahora - this.ultimoTimestamp) * 1000; // en microsegundos
const usoCPU = process.cpuUsage(this.ultimaMedicion);
// Calcular porcentaje de uso
const totalCPU = usoCPU.user + usoCPU.system;
const porcentaje = (totalCPU / tiempoTranscurrido) * 100;
const medicion = {
timestamp: new Date(),
porcentaje: porcentaje,
user: usoCPU.user,
system: usoCPU.system,
total: totalCPU,
};
this.estadisticas.push(medicion);
this.ultimaMedicion = process.cpuUsage();
this.ultimoTimestamp = ahora;
// Mantener solo últimas 50 mediciones
if (this.estadisticas.length > 50) {
this.estadisticas.shift();
}
return medicion;
}
obtenerPromedio() {
if (this.estadisticas.length === 0) return 0;
const suma = this.estadisticas.reduce(
(acc, curr) => acc + curr.porcentaje,
0
);
return suma / this.estadisticas.length;
}
mostrarEstadisticas() {
const promedio = this.obtenerPromedio();
const ultima = this.estadisticas[this.estadisticas.length - 1];
console.log("\n=== ESTADÍSTICAS DE CPU ===");
console.log(`Uso actual: ${ultima.porcentaje.toFixed(2)}%`);
console.log(`Promedio: ${promedio.toFixed(2)}%`);
console.log(`User: ${(ultima.user / 1000000).toFixed(3)}s`);
console.log(`System: ${(ultima.system / 1000000).toFixed(3)}s`);
console.log(`Total: ${(ultima.total / 1000000).toFixed(3)}s`);
}
}
// EJEMPLO 6: MONITOREO INTEGRADO COMPLETO
class MonitorCompleto {
constructor(config = {}) {
this.config = {
intervaloMemoria: config.intervaloMemoria || 5000,
intervaloCPU: config.intervaloCPU || 2000,
maxMediciones: config.maxMediciones || 100,
...config,
};
this.datosMemoria = [];
this.datosCPU = [];
this.intervalos = [];
}
iniciar() {
console.log("Iniciando monitor completo...");
// Monitoreo de memoria
const intervaloMemoria = setInterval(() => {
this.registrarMemoria();
}, this.config.intervaloMemoria);
// Monitoreo de CPU
const intervaloCPU = setInterval(() => {
this.registrarCPU();
}, this.config.intervaloCPU);
this.intervalos.push(intervaloMemoria, intervaloCPU);
}
detener() {
this.intervalos.forEach(clearInterval);
console.log("Monitor detenido");
}
registrarMemoria() {
const memoria = process.memoryUsage();
const dato = {
timestamp: new Date(),
rss: memoria.rss,
heapTotal: memoria.heapTotal,
heapUsed: memoria.heapUsed,
external: memoria.external,
};
this.datosMemoria.push(dato);
if (this.datosMemoria.length > this.config.maxMediciones) {
this.datosMemoria.shift();
}
// Mostrar resumen cada 5 mediciones
if (this.datosMemoria.length % 5 === 0) {
this.mostrarResumenMemoria();
}
}
registrarCPU() {
// Para CPU necesitamos implementar lógica similar al MonitorCPU
// Implementación simplificada para el ejemplo
const usoCPU = process.cpuUsage();
const dato = {
timestamp: new Date(),
user: usoCPU.user,
system: usoCPU.system,
};
this.datosCPU.push(dato);
if (this.datosCPU.length > this.config.maxMediciones) {
this.datosCPU.shift();
}
}
mostrarResumenMemoria() {
const ultima = this.datosMemoria[this.datosMemoria.length - 1];
const heapMB = Math.round(ultima.heapUsed / 1024 / 1024);
const rssMB = Math.round(ultima.rss / 1024 / 1024);
console.log(
`[${new Date().toLocaleTimeString()}] Memoria: Heap=${heapMB}MB, RSS=${rssMB}MB`
);
}
generarReporte() {
console.log("\n=== REPORTE COMPLETO DE MONITOREO ===");
if (this.datosMemoria.length === 0) {
console.log("No hay datos de monitoreo disponibles");
return;
}
// Estadísticas de memoria
const heapUsados = this.datosMemoria.map((d) => d.heapUsed);
const heapMin = Math.min(...heapUsados);
const heapMax = Math.max(...heapUsados);
const heapAvg = heapUsados.reduce((a, b) => a + b, 0) / heapUsados.length;
console.log("ESTADÍSTICAS DE MEMORIA:");
console.log(`Mínimo: ${Math.round(heapMin / 1024 / 1024)} MB`);
console.log(`Máximo: ${Math.round(heapMax / 1024 / 1024)} MB`);
console.log(`Promedio: ${Math.round(heapAvg / 1024 / 1024)} MB`);
console.log(`Mediciones: ${this.datosMemoria.length}`);
// Estadísticas de CPU
if (this.datosCPU.length > 0) {
const cpuTotales = this.datosCPU.map((d) => d.user + d.system);
const cpuAvg = cpuTotales.reduce((a, b) => a + b, 0) / cpuTotales.length;
console.log("ESTADÍSTICAS DE CPU:");
console.log(` Uso promedio: ${(cpuAvg / 1000000).toFixed(3)} segundos`);
}
}
}
// EJEMPLO 7: USO PRÁCTICO CON MANEJO DE ERRORES
function monitoreoRobusto() {
console.log("\n=== MONITOREO ROBUSTO CON MANEJO DE ERRORES ===");
try {
const memoria = process.memoryUsage();
if (!memoria || typeof memoria !== "object") {
throw new Error("process.memoryUsage() no retornó un objeto válido");
}
const metricasRequeridas = ["rss", "heapTotal", "heapUsed", "external"];
for (const metrica of metricasRequeridas) {
if (typeof memoria[metrica] !== "number") {
throw new Error(`Métrica ${metrica} no es un número`);
}
if (memoria[metrica] < 0) {
throw new Error(`Métrica ${metrica} tiene valor negativo`);
}
}
console.log("Monitoreo de memoria: OK");
console.log(`RSS: ${Math.round(memoria.rss / 1024 / 1024)} MB`);
} catch (error) {
console.error("Error en monitoreo:", error.message);
// En producción, podrías enviar esta alerta a un servicio de monitoring
}
}
// EJEMPLO 8: SIMULACIÓN DE ESTRÉS DE MEMORIA
function simulacionEstresMemoria() {
console.log("\n=== SIMULACIÓN DE ESTRÉS DE MEMORIA ===");
console.log("Creando objetos para simular uso intensivo de memoria...");
const objetos = [];
const memoriaInicial = process.memoryUsage().heapUsed;
// Crear muchos objetos grandes
for (let i = 0; i < 10000; i++) {
objetos.push({
id: i,
data: Buffer.alloc(1024), // 1KB por objeto
metadata: {
created: new Date(),
index: i,
largeString: "x".repeat(500),
},
});
// Mostrar progreso cada 1000 objetos
if (i % 1000 === 0) {
const memoriaActual = process.memoryUsage().heapUsed;
const incremento = memoriaActual - memoriaInicial;
console.log(
`Creados ${i} objetos - Memoria: +${Math.round(
incremento / 1024 / 1024
)} MB`
);
}
}
const memoriaFinal = process.memoryUsage().heapUsed;
const totalIncremento = memoriaFinal - memoriaInicial;
console.log(`\nRESULTADO SIMULACIÓN:`);
console.log(`Objetos creados: ${objetos.length}`);
console.log(
`Incremento de memoria: ${Math.round(totalIncremento / 1024 / 1024)} MB`
);
console.log(
`Memoria por objeto: ${Math.round(
totalIncremento / objetos.length / 1024
)} KB`
);
// Limpiar
objetos.length = 0;
if (global.gc) global.gc();
console.log("Memoria liberada después de limpieza");
}
// EJECUCIÓN DE EJEMPLOS
async function ejecutarEjemplos() {
console.log("=== EJEMPLOS DE MONITOREO DE MEMORIA Y CPU ===\n");
// Ejemplo básico
monitorearMemoria();
// Monitoreo avanzado
monitoreoAvanzado();
// Detector de memory leaks
const detector = new DetectorMemoryLeaks(3000);
detector.iniciar();
// Esperar un poco para que el detector recopile datos
await new Promise((resolve) => setTimeout(resolve, 10000));
const stats = detector.obtenerEstadisticas();
console.log("\nEstadísticas del detector:", stats);
detector.detener();
// Monitor completo
const monitorCompleto = new MonitorCompleto();
monitorCompleto.iniciar();
// Esperar para recopilar datos
await new Promise((resolve) => setTimeout(resolve, 8000));
monitorCompleto.generarReporte();
monitorCompleto.detener();
// Monitoreo robusto
monitoreoRobusto();
// Benchmark (opcional - puede usar mucha memoria)
// benchmarkMemoria(50000);
console.log("\n=== TODOS LOS EJEMPLOS COMPLETADOS ===");
}
// Descomentar para ejecutar todos los ejemplos
// ejecutarEjemplos();
Conceptos principales
- RSS (Resident Set Size): Memoria física total usada por el proceso
- Heap Total: Memoria total asignada al heap de V8
- Heap Used: Memoria actualmente usada en el heap de V8
- External: Memoria usada por objetos C++ vinculados a JavaScript
Usos comunes
- Detección de memory leaks: Crecimiento continuo de heap usado
- Optimización de memoria: Identificar patrones de uso intensivo
- Capacity planning: Planificar recursos necesarios
- Debugging: Identificar problemas de rendimiento
Mejores prácticas
- Monitorear continuamente en producción
- Establecer límites y alertas
- Usar porcentajes además de valores absolutos
- Considerar el garbage collection en las mediciones
- Implementar monitoreo robusto con manejo de errores
Herramientas complementarias
- node --inspect: Debugger integrado
- clinic.js: Suite de profiling
- v8-profiler: Profiler específico de V8
- heapdump: Volcados de heap para análisis
Flujos estándar: process.stdin, process.stdout y process.stderr
Node.js ofrece tres flujos predeterminados que permiten interactuar con el usuario mediante texto. Estos flujos son muy útiles para crear programas de consola, asistentes interactivos o herramientas administrativas.
process.stdin representa el flujo de entrada estándar. Permite leer datos que el usuario escribe en la terminal.
process.stdout es el flujo de salida estándar. Se utiliza para mostrar información en la consola.
process.stderr representa el flujo de error estándar. Su finalidad es mostrar mensajes de error o advertencias.
Ejemplo práctico donde el programa pregunta al usuario por su nombre y responde de manera asíncrona usando streams.
// archivo: entrada_salida.js
// este ejemplo crea una pequeña interacción de consola usando stdin y stdout
async function leerLinea() {
// devolvemos una promesa para trabajar con async/await
return new Promise((resolve) => {
process.stdin.once("data", (data) => {
// data contiene un buffer; lo convertimos a texto y eliminamos saltos de línea
resolve(data.toString().trim());
});
});
}
async function main() {
console.log("¿cómo te llamas?");
// esperamos a que el usuario escriba algo
const nombre = await leerLinea();
// enviamos la respuesta usando stdout
process.stdout.write(`hola ${nombre}, bienvenido\n`);
}
await main();
Casos de uso reales
Estas técnicas son esenciales para herramientas de administración, instaladores interactivos, generadores de proyectos y scripts automatizados que requieren interacción humana.
Consideraciones de seguridad
Cuando leas datos por stdin, valida siempre lo que recibes. Nunca ejecutes comandos del sistema directamente basados en lo que el usuario escriba sin filtrar y asegurar el contenido.
Módulo OS - Información del Sistema Operativo
El módulo os proporciona información detallada sobre el sistema operativo y recursos del sistema.
// Importar el módulo 'os' (Operating System) de Node.js
// Este módulo proporciona métodos utilidades relacionadas con el sistema operativo
import os from "os";
function mostrarInfoSistema() {
console.log("=== INFORMACIÓN DEL SISTEMA OPERATIVO ===");
// Información básica del sistema
// os.platform() - Retorna la plataforma del sistema operativo
// Valores comunes: 'darwin' (macOS), 'win32' (Windows), 'linux' (Linux)
console.log("Sistema operativo:", os.platform());
// os.type() - Retorna el nombre del sistema operativo
// Ejemplos: 'Linux', 'Darwin' (macOS), 'Windows_NT'
console.log("Tipo de sistema:", os.type());
// os.release() - Retorna la versión del kernel o sistema operativo
console.log("Versión del sistema:", os.release());
// os.arch() - Retorna la arquitectura del procesador
// Valores comunes: 'x64', 'arm', 'arm64', 'ia32'
console.log("Arquitectura:", os.arch());
// os.uptime() - Tiempo que el sistema ha estado ejecutándose en segundos
// Convertimos a horas para mejor legibilidad
console.log(
"Tiempo de actividad del sistema:",
Math.round(os.uptime() / 3600) + " horas"
);
// Información de memoria
// os.totalmem() - Memoria RAM total en bytes
console.log(
"Memoria total:",
Math.round(os.totalmem() / 1024 / 1024 / 1024) + " GB"
);
// os.freemem() - Memoria RAM disponible en bytes
console.log("Memoria libre:", Math.round(os.freemem() / 1024 / 1024) + " MB");
// Cálculo del porcentaje de memoria libre
console.log(
"Porcentaje de memoria libre:",
Math.round((os.freemem() / os.totalmem()) * 100) + "%"
);
// Información de CPU
// os.cpus() - Retorna un array con información de cada CPU/core
const cpus = os.cpus();
console.log("Número de CPUs:", cpus.length);
console.log("Modelo de CPU:", cpus[0].model);
console.log("Velocidad de CPU:", cpus[0].speed + " MHz");
// Información de red
// os.hostname() - Nombre del host del sistema
console.log("Hostname:", os.hostname());
console.log("Interfaces de red:");
// os.networkInterfaces() - Retorna información de las interfaces de red
const interfaces = os.networkInterfaces();
// Iterar sobre todas las interfaces de red
Object.entries(interfaces).forEach(([nombre, direcciones]) => {
direcciones.forEach((direccion) => {
// Filtrar solo direcciones IPv4 que no sean internas
if (direccion.family === "IPv4" && !direccion.internal) {
console.log(` ${nombre}: ${direccion.address}`);
}
});
});
// Información del usuario y directorios
// os.homedir() - Directorio home del usuario actual
console.log("Directorio home:", os.homedir());
// os.tmpdir() - Directorio temporal del sistema
console.log("Directorio temporal:", os.tmpdir());
// os.userInfo() - Información del usuario actual
console.log("Usuario actual:", os.userInfo().username);
}
// Ejecutar la función
mostrarInfoSistema();
// EJEMPLOS ADICIONALES Y EXPLICACIONES DETALLADAS:
// EJEMPLO 1: INFORMACIÓN DETALLADA DE CPU
function mostrarInfoCPUDetallada() {
console.log("\n=== INFORMACIÓN DETALLADA DE CPU ===");
const cpus = os.cpus();
cpus.forEach((cpu, index) => {
console.log(`CPU ${index + 1}:`);
console.log(`Modelo: ${cpu.model}`);
console.log(`Velocidad: ${cpu.speed} MHz`);
console.log(`Tiempos (en milisegundos):`);
console.log(`Usuario: ${cpu.times.user}`);
console.log(`Sistema: ${cpu.times.sys}`);
console.log(`Inactivo: ${cpu.times.idle}`);
console.log(`IRQ: ${cpu.times.irq}`);
// Calcular porcentaje de uso
const total =
cpu.times.user + cpu.times.sys + cpu.times.idle + cpu.times.irq;
const usoPorcentaje = (
((cpu.times.user + cpu.times.sys) / total) *
100
).toFixed(1);
console.log(`Uso: ${usoPorcentaje}%`);
});
console.log(`Total de cores: ${cpus.length}`);
console.log(
`Núcleos físicos: ${
cpus.length / (cpus[0].model.includes("Intel") ? 2 : 1)
}`
);
}
// EJEMPLO 2: MONITOREO DE MEMORIA EN TIEMPO REAL
function monitoreoMemoriaTiempoReal() {
console.log("\n=== MONITOREO DE MEMORIA EN TIEMPO REAL ===");
const memoria = {
total: os.totalmem(),
libre: os.freemem(),
usada: os.totalmem() - os.freemem(),
};
console.log("Estado actual de memoria:");
console.log("Total:", (memoria.total / 1024 / 1024 / 1024).toFixed(2), "GB");
console.log("Usada:", (memoria.usada / 1024 / 1024 / 1024).toFixed(2), "GB");
console.log("Libre:", (memoria.libre / 1024 / 1024 / 1024).toFixed(2), "GB");
console.log(
"Porcentaje usado:",
((memoria.usada / memoria.total) * 100).toFixed(1) + "%"
);
console.log(
"Porcentaje libre:",
((memoria.libre / memoria.total) * 100).toFixed(1) + "%"
);
// Alertas basadas en umbrales
const porcentajeLibre = (memoria.libre / memoria.total) * 100;
if (porcentajeLibre < 10) {
console.log("ALERTA: Menos del 10% de memoria libre disponible");
} else if (porcentajeLibre < 20) {
console.log("ADVERTENCIA: Menos del 20% de memoria libre disponible");
}
}
// EJEMPLO 3: ANÁLISIS DE INTERFACES DE RED
function analizarInterfacesRed() {
console.log("\n=== ANÁLISIS DETALLADO DE INTERFACES DE RED ===");
const interfaces = os.networkInterfaces();
Object.entries(interfaces).forEach(([nombre, direcciones]) => {
console.log(`Interfaz: ${nombre}`);
direcciones.forEach((direccion) => {
console.log(`Familia: ${direccion.family}`);
console.log(`Dirección: ${direccion.address}`);
console.log(`Máscara de red: ${direccion.netmask}`);
console.log(`Interna: ${direccion.internal}`);
console.log(`MAC: ${direccion.mac || "No disponible"}`);
console.log(`CIDR: ${direccion.cidr || "No disponible"}`);
console.log(" ---");
});
});
// Contar tipos de interfaces
let ipv4Count = 0;
let ipv6Count = 0;
let externas = 0;
let internas = 0;
Object.values(interfaces).forEach((direcciones) => {
direcciones.forEach((direccion) => {
if (direccion.family === "IPv4") ipv4Count++;
if (direccion.family === "IPv6") ipv6Count++;
if (direccion.internal) internas++;
else externas++;
});
});
console.log("Resumen de interfaces:");
console.log(`IPv4: ${ipv4Count}`);
console.log(`IPv6: ${ipv6Count}`);
console.log(`Internas: ${internas}`);
console.log(`Externas: ${externas}`);
}
// EJEMPLO 4: INFORMACIÓN DEL SISTEMA PARA LOGGING
function generarReporteSistema() {
console.log("\n=== REPORTE COMPLETO DEL SISTEMA ===");
const reporte = {
timestamp: new Date().toISOString(),
sistema: {
plataforma: os.platform(),
tipo: os.type(),
version: os.release(),
arquitectura: os.arch(),
uptime: os.uptime(),
hostname: os.hostname(),
},
memoria: {
total: os.totalmem(),
libre: os.freemem(),
usada: os.totalmem() - os.freemem(),
},
cpu: {
nucleos: os.cpus().length,
modelo: os.cpus()[0].model,
velocidad: os.cpus()[0].speed,
},
usuario: {
nombre: os.userInfo().username,
homedir: os.homedir(),
tmpdir: os.tmpdir(),
},
};
// Convertir a formato legible
console.log("Reporte del sistema:");
console.log("Timestamp:", reporte.timestamp);
console.log(
"Sistema:",
`${reporte.sistema.plataforma} ${reporte.sistema.arquitectura}`
);
console.log(
"Memoria:",
`${Math.round(reporte.memoria.total / 1024 / 1024 / 1024)} GB total`
);
console.log("CPU:", `${reporte.cpu.nucleos} cores - ${reporte.cpu.modelo}`);
console.log("Usuario:", reporte.usuario.nombre);
return reporte;
}
// EJEMPLO 5: DETECCIÓN DE CARACTERÍSTICAS DEL SISTEMA
function detectarCaracteristicas() {
console.log("\n=== DETECCIÓN DE CARACTERÍSTICAS DEL SISTEMA ===");
const caracteristicas = {
esWindows: os.platform() === "win32",
esLinux: os.platform() === "linux",
esMacOS: os.platform() === "darwin",
es64Bits: os.arch() === "x64",
tieneSuficienteMemoria: os.totalmem() > 2 * 1024 * 1024 * 1024, // 2GB
tieneMultiplesCores: os.cpus().length > 1,
sistemaReciente: os.uptime() < 30 * 24 * 3600, // Menos de 30 días
};
console.log("Características detectadas:");
Object.entries(caracteristicas).forEach(([caracteristica, valor]) => {
console.log(` ${caracteristica}: ${valor}`);
});
// Recomendaciones basadas en características
if (caracteristicas.esWindows) {
console.log("Recomendación: Sistema Windows detectado");
}
if (!caracteristicas.tieneSuficienteMemoria) {
console.log(
"Advertencia: Memoria RAM insuficiente para aplicaciones pesadas"
);
}
if (caracteristicas.tieneMultiplesCores) {
console.log(
"Información: Sistema con múltiples cores, adecuado para procesamiento paralelo"
);
}
}
// EJEMPLO 6: COMPARACIÓN ENTRE SISTEMAS
function compararConRequisitos(requisitos) {
console.log("\n=== VERIFICACIÓN DE REQUISITOS DEL SISTEMA ===");
const requisitosMinimos = {
memoriaMinima: requisitos?.memoriaMinima || 4 * 1024 * 1024 * 1024, // 4GB por defecto
nucleosMinimos: requisitos?.nucleosMinimos || 2,
plataformasSoportadas: requisitos?.plataformasSoportadas || [
"linux",
"darwin",
"win32",
],
};
const sistemaActual = {
memoria: os.totalmem(),
nucleos: os.cpus().length,
plataforma: os.platform(),
};
console.log("Requisitos mínimos:");
console.log(
`Memoria: ${requisitosMinimos.memoriaMinima / 1024 / 1024 / 1024} GB`
);
console.log(`Núcleos: ${requisitosMinimos.nucleosMinimos}`);
console.log(
`Plataformas: ${requisitosMinimos.plataformasSoportadas.join(", ")}`
);
console.log("Sistema actual:");
console.log(
` Memoria: ${(sistemaActual.memoria / 1024 / 1024 / 1024).toFixed(1)} GB`
);
console.log(`Núcleos: ${sistemaActual.nucleos}`);
console.log(`Plataforma: ${sistemaActual.plataforma}`);
// Verificar cumplimiento
const cumpleMemoria =
sistemaActual.memoria >= requisitosMinimos.memoriaMinima;
const cumpleNucleos =
sistemaActual.nucleos >= requisitosMinimos.nucleosMinimos;
const cumplePlataforma = requisitosMinimos.plataformasSoportadas.includes(
sistemaActual.plataforma
);
console.log("Resultado de verificación:");
console.log(`Memoria: ${cumpleMemoria ? "CUMPLE" : "NO CUMPLE"}`);
console.log(`Núcleos: ${cumpleNucleos ? "CUMPLE" : "NO CUMPLE"}`);
console.log(`Plataforma: ${cumplePlataforma ? "CUMPLE" : "NO CUMPLE"}`);
const cumpleTodos = cumpleMemoria && cumpleNucleos && cumplePlataforma;
console.log(
`Requisitos mínimos: ${cumpleTodos ? "CUMPLIDOS" : "NO CUMPLIDOS"}`
);
return cumpleTodos;
}
// EJEMPLO 7: INFORMACIÓN DE CARGA DEL SISTEMA
function mostrarCargaSistema() {
console.log("\n=== CARGA ACTUAL DEL SISTEMA ===");
// La carga del sistema está representada por el promedio de carga
// en 1, 5 y 15 minutos (solo disponible en Unix/Linux)
const carga = os.loadavg();
console.log("Promedio de carga:");
console.log(`1 minuto: ${carga[0].toFixed(2)}`);
console.log(`5 minutos: ${carga[1].toFixed(2)}`);
console.log(`15 minutos: ${carga[2].toFixed(2)}`);
// Interpretación de la carga (para sistemas Unix/Linux)
const nucleos = os.cpus().length;
console.log(`Núcleos disponibles: ${nucleos}`);
if (carga[0] > nucleos) {
console.log("Estado: El sistema está sobrecargado (carga > núcleos)");
} else if (carga[0] > nucleos * 0.7) {
console.log("Estado: El sistema tiene carga alta");
} else {
console.log("Estado: El sistema tiene carga normal");
}
// Información de memoria swap (si está disponible)
try {
// En algunos sistemas, esta información puede no estar disponible
console.log("Memoria swap información disponible");
} catch (error) {
console.log("Información de swap no disponible en este sistema");
}
}
// EJEMPLO 8: CLASE PARA GESTIÓN DE INFORMACIÓN DEL SISTEMA
class GestorSistema {
constructor() {
this.ultimaActualizacion = new Date();
this.datos = this.obtenerDatosCompletos();
}
obtenerDatosCompletos() {
return {
sistema: {
plataforma: os.platform(),
tipo: os.type(),
version: os.release(),
arquitectura: os.arch(),
uptime: os.uptime(),
hostname: os.hostname(),
},
memoria: {
total: os.totalmem(),
libre: os.freemem(),
usada: os.totalmem() - os.freemem(),
porcentajeLibre: (os.freemem() / os.totalmem()) * 100,
},
cpu: {
nucleos: os.cpus().length,
modelo: os.cpus()[0]?.model || "Desconocido",
velocidad: os.cpus()[0]?.speed || 0,
detalles: os.cpus(),
},
red: {
interfaces: os.networkInterfaces(),
},
usuario: {
info: os.userInfo(),
homedir: os.homedir(),
tmpdir: os.tmpdir(),
},
carga: os.loadavg(),
};
}
actualizar() {
this.datos = this.obtenerDatosCompletos();
this.ultimaActualizacion = new Date();
}
generarReporte() {
console.log("\n=== REPORTE DEL GESTOR DE SISTEMA ===");
console.log(
"Última actualización:",
this.ultimaActualizacion.toLocaleString()
);
console.log("Plataforma:", this.datos.sistema.plataforma);
console.log(
"Memoria libre:",
Math.round(this.datos.memoria.porcentajeLibre) + "%"
);
console.log("Núcleos CPU:", this.datos.cpu.nucleos);
console.log(
"Interfaces de red:",
Object.keys(this.datos.red.interfaces).length
);
}
verificarSalud() {
const alertas = [];
// Verificar memoria
if (this.datos.memoria.porcentajeLibre < 10) {
alertas.push("Memoria crítica: menos del 10% libre");
}
// Verificar carga del sistema
if (this.datos.carga[0] > this.datos.cpu.nucleos) {
alertas.push("Sistema sobrecargado");
}
// Verificar uptime (si es muy largo, podría necesitar reinicio)
if (this.datos.sistema.uptime > 90 * 24 * 3600) {
// 90 días
alertas.push("Sistema no reiniciado en más de 90 días");
}
return alertas;
}
}
// EJEMPLO 9: USO DEL GESTOR DE SISTEMA
function usarGestorSistema() {
console.log("\n=== USO DEL GESTOR DE SISTEMA ===");
const gestor = new GestorSistema();
gestor.generarReporte();
const alertas = gestor.verificarSalud();
if (alertas.length > 0) {
console.log("Alertas de salud del sistema:");
alertas.forEach((alerta) => console.log(" -", alerta));
} else {
console.log("Sistema en estado saludable");
}
// Simular actualización después de un tiempo
setTimeout(() => {
console.log("\nActualizando datos del sistema...");
gestor.actualizar();
gestor.generarReporte();
}, 2000);
}
// EJEMPLO 10: UTILIDADES PRÁCTICAS DEL SISTEMA
function utilidadesPracticas() {
console.log("\n=== UTILIDADES PRÁCTICAS ===");
// Obtener el endianness del sistema
console.log("Endianness:", os.endianness());
// Obtener información de las constantes del sistema
console.log(
"Constantes de señal disponibles:",
Object.keys(os.constants.signal || {}).length
);
console.log(
"Constantes de error disponibles:",
Object.keys(os.constants.errno || {}).length
);
// Información útil para desarrollo
console.log(
"Separador de ruta:",
JSON.stringify(os.platform() === "win32" ? ";" : ":")
);
console.log(
"Separador de directorio:",
JSON.stringify(os.platform() === "win32" ? "\\" : "/")
);
console.log("Línea de fin:", JSON.stringify(os.EOL));
// Información para optimización
console.log(
"Tamaño de página de memoria:",
os.totalmem() > 8 * 1024 * 1024 * 1024 ? "Grande" : "Normal"
);
}
// EJECUCIÓN DE EJEMPLOS
function ejecutarEjemplos() {
console.log("=== EJEMPLOS DE INFORMACIÓN DEL SISTEMA OPERATIVO ===\n");
// Ejemplo básico
mostrarInfoSistema();
// Ejemplos adicionales
mostrarInfoCPUDetallada();
monitoreoMemoriaTiempoReal();
analizarInterfacesRed();
generarReporteSistema();
detectarCaracteristicas();
// Verificar requisitos
compararConRequisitos({
memoriaMinima: 2 * 1024 * 1024 * 1024, // 2GB
nucleosMinimos: 2,
plataformasSoportadas: ["linux", "darwin", "win32"],
});
mostrarCargaSistema();
usarGestorSistema();
utilidadesPracticas();
console.log("\n=== TODOS LOS EJEMPLOS COMPLETADOS ===");
}
// Descomentar para ejecutar todos los ejemplos
// ejecutarEjemplos();
Métodos principales de OS
- Información del Sistema:
platform(),type(),release(),arch(),uptime() - Memoria:
totalmem(),freemem() - CPU:
cpus(),loadavg() - Red:
networkInterfaces(),hostname() - Usuario y Directorios:
homedir(),tmpdir(),userInfo() - Constantes:
constants,EOL,endianness()
Información clave
- Plataforma y Arquitectura: Determina compatibilidad binaria
- Memoria: Crucial para aplicaciones que manejan grandes datasets
- CPU: Importante para aplicaciones de procesamiento intensivo
- Red: Esencial para aplicaciones de red y servidores
- Usuario: Para personalización y permisos de archivos
Usos comunes
- Optimización: Adaptar el comportamiento según los recursos disponibles
- Logging: Incluir información del sistema en logs para debugging
- Monitoring: Verificar salud del sistema y recursos
- Compatibilidad: Detectar características para habilitar/deshabilitar funcionalidades
- Configuración: Ajustar parámetros según la capacidad del sistema
Consideraciones de portabilidad
- Algunos métodos como
loadavg()solo funcionan en sistemas Unix/Linux - Los formatos de rutas difieren entre Windows y Unix
- La información de CPU puede variar entre arquitecturas
- Las interfaces de red pueden tener diferentes nombres según el SO
Módulo Child Process - Ejecución de Procesos Hijos
El módulo child_process permite ejecutar comandos del sistema y otros programas desde Node.js. Node.js permite crear subprocesos que ejecutan archivos JavaScript de forma independiente y comunicarse con ellos mediante mensajes. Esto es útil para dividir tareas pesadas o paralelizar trabajos.
Antes de ver ejemplos prácticos, es importante comprender la idea: un proceso hijo es otro programa Node ejecutado por el programa principal. Ambos procesos pueden enviarse mensajes como si fueran eventos.
Ejecución Sincrónica de Comandos
// Importar execSync del módulo child_process
// execSync permite ejecutar comandos del sistema operativo de forma síncrona
// Esto significa que el código espera a que el comando termine antes de continuar
import { execSync } from "child_process";
function ejecutarComandosSincronos() {
try {
console.log("=== EJECUCIÓN SINCRÓNICA DE COMANDOS ===");
// Ejecutar comando simple
// execSync ejecuta el comando y retorna el resultado como Buffer o string
// { encoding: 'utf8' } convierte el resultado a string en lugar de Buffer
const resultado = execSync("ls -la", { encoding: "utf8" });
console.log("Listado de directorio:");
console.log(resultado);
// Ejecutar comando con parámetros
// .trim() elimina espacios y saltos de línea al inicio y final
const fecha = execSync("date", { encoding: "utf8" }).trim();
console.log("Fecha del sistema:", fecha);
// Obtener información del sistema
// El comando 'free -h' muestra memoria en formato legible (sistemas Unix/Linux)
const memoriaLibre = execSync("free -h", { encoding: "utf8" });
console.log("Memoria libre:");
console.log(memoriaLibre);
} catch (error) {
console.error("Error al ejecutar comando:", error.message);
}
}
// Ejecutar la función
ejecutarComandosSincronos();
// EJEMPLOS ADICIONALES Y EXPLICACIONES DETALLADAS:
// EJEMPLO 1: EJECUCIÓN CON MANEJO DE ERRORES DETALLADO
function ejecutarConManejoErrores() {
console.log("\n=== EJECUCIÓN CON MANEJO DE ERRORES DETALLADO ===");
try {
// Comando que podría fallar
const proceso = execSync("ps aux | grep node", {
encoding: "utf8",
stdio: "pipe", // Redirigir stdin, stdout, stderr
});
console.log("Procesos Node.js encontrados:");
console.log(proceso);
} catch (error) {
// Error más detallado
console.error("Error ejecutando comando:");
console.error("Mensaje:", error.message);
console.error("Código de salida:", error.status);
console.error("Señal:", error.signal);
console.error("Salida de error:", error.stderr);
}
}
// EJEMPLO 2: EJECUCIÓN CON OPCIONES AVANZADAS
function ejecutarConOpcionesAvanzadas() {
console.log("\n=== EJECUCIÓN CON OPCIONES AVANZADAS ===");
try {
// Ejecutar en un directorio específico
const archivosProyecto = execSync('find . -name "*.js" -type f', {
encoding: "utf8",
cwd: "./src", // Cambiar directorio de trabajo
});
console.log("Archivos JavaScript en src/:");
console.log(archivosProyecto);
// Ejecutar con timeout
const resultadoTimeout = execSync('sleep 2 && echo "Completado"', {
encoding: "utf8",
timeout: 1000, // Timeout de 1 segundo
});
console.log("Resultado con timeout:", resultadoTimeout);
} catch (error) {
if (error.code === "ETIMEDOUT") {
console.error("El comando excedió el tiempo límite");
} else {
console.error("Error:", error.message);
}
}
}
// EJEMPLO 3: COMANDOS MULTIPLATAFORMA
function comandosMultiplataforma() {
console.log("\n=== COMANDOS MULTIPLATAFORMA ===");
try {
// Detectar sistema operativo
const plataforma = process.platform;
let comando;
if (plataforma === "win32") {
comando = "dir";
} else {
comando = "ls -la";
}
const listado = execSync(comando, { encoding: "utf8" });
console.log(`Listado de directorio (${plataforma}):`);
console.log(listado);
} catch (error) {
console.error("Error en comando multiplataforma:", error.message);
}
}
// EJEMPLO 4: CAPTURA DE INFORMACIÓN DEL SISTEMA
function capturarInfoSistema() {
console.log("\n=== CAPTURA DE INFORMACIÓN DEL SISTEMA ===");
try {
// Información del sistema
const sistemaInfo = {
hostname: execSync("hostname", { encoding: "utf8" }).trim(),
kernel: execSync("uname -r", { encoding: "utf8" }).trim(),
usuarios: execSync("who | wc -l", { encoding: "utf8" }).trim(),
espacioDisco: execSync("df -h /", { encoding: "utf8" }).split("\n")[1],
};
console.log("Información del sistema:");
console.log("Hostname:", sistemaInfo.hostname);
console.log("Kernel:", sistemaInfo.kernel);
console.log("Usuarios conectados:", sistemaInfo.usuarios);
console.log("Espacio en disco raíz:", sistemaInfo.espacioDisco);
} catch (error) {
console.error("Error capturando información del sistema:", error.message);
}
}
// EJEMPLO 5: PROCESAMIENTO DE RESULTADOS
function procesarResultadosComandos() {
console.log("\n=== PROCESAMIENTO DE RESULTADOS DE COMANDOS ===");
try {
// Obtener lista de procesos y procesarla
const procesos = execSync("ps aux", { encoding: "utf8" });
const lineas = procesos.split("\n").slice(1); // Eliminar cabecera
console.log("Top 5 procesos por uso de memoria:");
lineas
.filter((linea) => linea.trim()) // Eliminar líneas vacías
.map((linea) => {
const columnas = linea.trim().split(/\s+/);
return {
usuario: columnas[0],
pid: columnas[1],
cpu: columnas[2],
mem: columnas[3],
comando: columnas.slice(10).join(" "),
};
})
.sort((a, b) => parseFloat(b.mem) - parseFloat(a.mem)) // Ordenar por memoria
.slice(0, 5) // Tomar solo los primeros 5
.forEach((proceso, index) => {
console.log(`${index + 1}. ${proceso.comando} (Mem: ${proceso.mem}%)`);
});
} catch (error) {
console.error("Error procesando resultados:", error.message);
}
}
// EJEMPLO 6: VALIDACIÓN DE DEPENDENCIAS DEL SISTEMA
function validarDependenciasSistema() {
console.log("\n=== VALIDACIÓN DE DEPENDENCIAS DEL SISTEMA ===");
const dependencias = [
{ nombre: "Git", comando: "git --version" },
{ nombre: "Node.js", comando: "node --version" },
{ nombre: "npm", comando: "npm --version" },
{ nombre: "Docker", comando: "docker --version" },
];
dependencias.forEach((dep) => {
try {
const version = execSync(dep.comando, {
encoding: "utf8",
stdio: ["pipe", "pipe", "ignore"], // Ignorar stderr
}).trim();
console.log(`${dep.nombre}: ${version}`);
} catch (error) {
console.log(`${dep.nombre}: No disponible`);
}
});
}
// EJEMPLO 7: EJECUCIÓN CON VARIABLES DE ENTORNO
function ejecutarConVariablesEntorno() {
console.log("\n=== EJECUCIÓN CON VARIABLES DE ENTORNO ===");
try {
// Ejecutar con variables de entorno personalizadas
const resultado = execSync("echo $MI_VARIABLE && echo $OTRA_VARIABLE", {
encoding: "utf8",
env: {
...process.env, // Mantener variables existentes
MI_VARIABLE: "valor_personalizado",
OTRA_VARIABLE: "otro_valor",
},
});
console.log("Salida del comando con variables personalizadas:");
console.log(resultado);
} catch (error) {
console.error("Error ejecutando con variables de entorno:", error.message);
}
}
// EJEMPLO 8: CLASE PARA GESTIÓN DE COMANDOS
class GestorComandos {
constructor() {
this.historial = [];
}
ejecutar(comando, opciones = {}) {
try {
const config = {
encoding: "utf8",
timeout: 30000,
...opciones,
};
console.log(`Ejecutando: ${comando}`);
const inicio = Date.now();
const resultado = execSync(comando, config);
const duracion = Date.now() - inicio;
// Registrar en historial
this.historial.push({
comando,
duracion,
timestamp: new Date(),
exito: true,
});
return {
exito: true,
salida: resultado,
duracion,
};
} catch (error) {
// Registrar error en historial
this.historial.push({
comando,
duracion: 0,
timestamp: new Date(),
exito: false,
error: error.message,
});
return {
exito: false,
error: error.message,
codigo: error.status,
};
}
}
obtenerEstadisticas() {
const total = this.historial.length;
const exitosos = this.historial.filter((c) => c.exito).length;
const fallidos = total - exitosos;
return {
totalComandos: total,
exitosos: exitosos,
fallidos: fallidos,
tasaExito: total > 0 ? ((exitosos / total) * 100).toFixed(1) + "%" : "0%",
};
}
mostrarHistorial() {
console.log("\nHistorial de comandos:");
this.historial.forEach((item, index) => {
const estado = item.exito ? "ok" : "error";
console.log(`${index + 1}. ${estado} ${item.comando}`);
if (!item.exito) {
console.log(` Error: ${item.error}`);
}
});
}
}
// EJEMPLO 9: USO DEL GESTOR DE COMANDOS
function usarGestorComandos() {
console.log("\n=== USO DEL GESTOR DE COMANDOS ===");
const gestor = new GestorComandos();
// Ejecutar varios comandos
const comandos = [
"pwd",
"whoami",
"uname -a",
"comando_inexistente", // Este fallará
];
comandos.forEach((comando) => {
const resultado = gestor.ejecutar(comando);
if (resultado.exito) {
console.log(`${comando}: Éxito (${resultado.duracion}ms)`);
} else {
console.log(`${comando}: Falló - ${resultado.error}`);
}
});
// Mostrar estadísticas
const stats = gestor.obtenerEstadisticas();
console.log("\nEstadísticas:");
console.log("Total comandos:", stats.totalComandos);
console.log("Exitosos:", stats.exitosos);
console.log("Fallidos:", stats.fallidos);
console.log("Tasa de éxito:", stats.tasaExito);
return gestor;
}
// EJEMPLO 10: SEGURIDAD EN EJECUCIÓN DE COMANDOS
function ejecucionSegura() {
console.log("\n=== EJECUCIÓN SEGURA DE COMANDOS ===");
// Lista blanca de comandos permitidos
const comandosPermitidos = ["date", "pwd", "whoami", "uname -a"];
function ejecutarComandoSeguro(comando) {
// Validar que el comando esté en la lista blanca
if (!comandosPermitidos.includes(comando)) {
console.error(`Comando no permitido: ${comando}`);
return { exito: false, error: "Comando no autorizado" };
}
// Validar que no contenga caracteres peligrosos
const caracteresPeligrosos = [";", "&", "|", "`", "$", ">", "<"];
if (caracteresPeligrosos.some((caracter) => comando.includes(caracter))) {
console.error(`Comando contiene caracteres peligrosos: ${comando}`);
return { exito: false, error: "Caracteres no permitidos" };
}
try {
const resultado = execSync(comando, {
encoding: "utf8",
timeout: 5000,
stdio: "pipe",
});
return { exito: true, salida: resultado };
} catch (error) {
return { exito: false, error: error.message };
}
}
// Probar comandos seguros
const comandosPrueba = ["date", "pwd", "ls -la"]; // El último no está permitido
comandosPrueba.forEach((comando) => {
console.log(`\nIntentando ejecutar: ${comando}`);
const resultado = ejecutarComandoSeguro(comando);
if (resultado.exito) {
console.log("Resultado:", resultado.salida.trim());
} else {
console.log("Error:", resultado.error);
}
});
}
// EJEMPLO 11: BACKUP AUTOMATIZADO CON COMANDOS
function backupAutomatizado() {
console.log("\n=== BACKUP AUTOMATIZADO CON COMANDOS ===");
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const nombreBackup = `backup-${timestamp}.tar.gz`;
console.log("Iniciando backup...");
// Crear backup de un directorio
const comandoBackup = `tar -czf ${nombreBackup} ./datos`;
const resultadoBackup = execSync(comandoBackup, { encoding: "utf8" });
console.log(`Backup creado: ${nombreBackup}`);
// Verificar que el backup se creó
const verificacion = execSync(`ls -la ${nombreBackup}`, {
encoding: "utf8",
});
console.log("Verificación:", verificacion.trim());
// Calcular tamaño
const tamaño = execSync(`du -h ${nombreBackup}`, {
encoding: "utf8",
}).split("\t")[0];
console.log(`Tamaño del backup: ${tamaño}`);
} catch (error) {
console.error("Error en backup automatizado:", error.message);
}
}
// EJEMPLO 12: MONITOREO DE RECURSOS DEL SISTEMA
function monitoreoRecursosSistema() {
console.log("\n=== MONITOREO DE RECURSOS DEL SISTEMA ===");
try {
const recursos = {
memoria: execSync("free -m", { encoding: "utf8" }),
disco: execSync("df -h", { encoding: "utf8" }),
carga: execSync("uptime", { encoding: "utf8" }),
procesos: execSync("ps aux --sort=-%cpu | head -5", { encoding: "utf8" }),
};
console.log("Estado de recursos del sistema:");
console.log("\nMemoria:");
console.log(recursos.memoria);
console.log("\nDisco:");
console.log(recursos.disco);
console.log("\nCarga del sistema:");
console.log(recursos.carga.trim());
console.log("\nTop 5 procesos por CPU:");
console.log(recursos.procesos);
} catch (error) {
console.error("Error en monitoreo de recursos:", error.message);
}
}
// EJECUCIÓN DE EJEMPLOS
function ejecutarEjemplos() {
console.log("=== EJEMPLOS DE EJECUCIÓN DE COMANDOS DEL SISTEMA ===\n");
// Ejemplo básico
ejecutarComandosSincronos();
// Ejemplos adicionales
ejecutarConManejoErrores();
ejecutarConOpcionesAvanzadas();
comandosMultiplataforma();
capturarInfoSistema();
procesarResultadosComandos();
validarDependenciasSistema();
ejecutarConVariablesEntorno();
// Usar gestor de comandos
const gestor = usarGestorComandos();
// Ejecución segura
ejecucionSegura();
// Backup automatizado (comentado para seguridad)
// backupAutomatizado();
// Monitoreo de recursos
monitoreoRecursosSistema();
console.log("\n=== TODOS LOS EJEMPLOS COMPLETADOS ===");
}
// Descomentar para ejecutar todos los ejemplos
// ejecutarEjemplos();
Caracteristicas principales de ExecSync
- Ejecución Síncrona: El código espera a que el comando termine
- Retorno de Resultados: Devuelve el output del comando como Buffer o string
- Manejo de Errores: Lanza excepciones cuando los comandos fallan
- Opciones de Configuración: encoding, cwd, timeout, env, stdio
Opciones de configuración
encoding: Convierte el output a string ('utf8', 'ascii', etc.)cwd: Cambia el directorio de trabajo para el comandotimeout: Tiempo máximo de ejecución en milisegundosenv: Variables de entorno personalizadasstdio: Configuración de stdin, stdout, stderr
Consideraciones de seguridad
- Validar y sanitizar entradas de usuario
- Usar lista blanca de comandos permitidos
- Evitar comandos con entrada de usuario sin sanitizar
- Considerar el uso de
execFileSyncpara mayor seguridad - Limitar permisos y tiempos de ejecución
Casos de uso comunes
- Automatización de tareas del sistema
- Scripts de deployment y build
- Monitoreo de recursos del sistema
- Validación de dependencias
- Generación de reportes del sistema
- Tareas de administración
Limitaciones y alternativas
execSync: Bloquea el event loop, no adecuado para comandos largosexec: Versión asíncrona para no bloquearspawnSync: Más control sobre stdin/stdoutexecFileSync: Más seguro para comandos sin shell
Ejecución Asincrónica de Comandos
// Importamos la función 'exec' del módulo 'child_process'
// Este módulo permite ejecutar comandos del sistema operativo desde Node.js
// Usamos ESModules (import/export) en lugar de CommonJS (require)
import { exec } from "child_process";
// Función que ejecuta un comando del sistema de forma asíncrona
// Retorna una Promise para manejar la operación asíncrona
function ejecutarComandoAsincrono(comando) {
// Creamos y retornamos una nueva Promise
// La Promise recibe dos funciones: resolve (éxito) y reject (error)
return new Promise((resolve, reject) => {
// Mostramos en consola qué comando se está ejecutando
console.log(`Ejecutando comando: ${comando}`);
// Ejecutamos el comando del sistema operativo
// exec recibe tres parámetros: comando, opciones y función callback
exec(comando, { encoding: "utf8" }, (error, stdout, stderr) => {
// El callback se ejecuta cuando el comando termina
// error: contiene información si hubo un error
// stdout: salida estándar del comando (lo que normalmente ves en terminal)
// stderr: salida de error del comando (mensajes de error del comando)
if (error) {
// Si hubo un error en la ejecución del comando
console.error(`Error ejecutando comando: ${error.message}`);
// Rechazamos la Promise con el error
reject(error);
return; // Salimos de la función
}
if (stderr) {
// Si el comando escribió en stderr (aunque no necesariamente falló)
console.error(`Error estándar: ${stderr}`);
// Nota: No rechazamos aquí porque algunos comandos usan stderr para advertencias
}
// Mostramos la salida exitosa del comando
console.log("Salida del comando:");
console.log(stdout);
// Resolvemos la Promise con la salida del comando
resolve(stdout);
});
});
}
// Función asíncrona que demuestra el uso de comandos
// 'async' permite usar 'await' dentro de la función
async function demostracionComandosAsincronos() {
try {
// await pausa la ejecución hasta que la Promise se resuelva
// Los comandos se ejecutan secuencialmente, uno después del otro
// Ejecuta 'pwd' - muestra el directorio actual de trabajo
await ejecutarComandoAsincrono("pwd");
// Ejecuta 'whoami' - muestra el usuario actual del sistema
await ejecutarComandoAsincrono("whoami");
// Ejecuta 'echo' - imprime un mensaje en consola
await ejecutarComandoAsincrono('echo "Hola desde Node.js"');
} catch (error) {
// Capturamos cualquier error que ocurra en el bloque try
console.error("Error en demostración:", error.message);
}
}
// Ejecutamos la función de demostración
// Esto inicia la secuencia de comandos
demostracionComandosAsincronos();
Explicación conceptual
Este código demuestra cómo ejecutar comandos del sistema operativo desde Node.js de forma asíncrona y controlada. La clave está en:
- Conversión de callback a Promise:
execusa callbacks, pero nosotros lo envolvemos en una Promise para un manejo más moderno. - Control de errores: Maneja tanto errores de ejecución como salidas de error del comando.
- Ejecución secuencial: Usando
async/awaitgarantizamos que los comandos se ejecuten en orden.
Flujo de ejecución
- Se ejecuta
pwdy muestra el directorio actual - Luego
whoamimuestra el usuario - Finalmente
echoimprime el mensaje - Si algún comando falla, se captura en el bloque catch
Spawn para Comandos con Salida en Tiempo Real
// Importamos la función 'spawn' del módulo 'child_process'
// A diferencia de 'exec', 'spawn' es más eficiente para comandos que producen
// grandes cantidades de datos o salida continua
import { spawn } from "child_process";
// Función que ejecuta un comando con spawn y maneja la salida en tiempo real
// spawn recibe el comando y sus argumentos por separado, lo que es más seguro
function ejecutarComandoTiempoReal(comando, argumentos = []) {
// Retornamos una Promise para manejo asíncrono
return new Promise((resolve, reject) => {
// Mostramos el comando completo que se va a ejecutar
console.log(`Ejecutando: ${comando} ${argumentos.join(" ")}`);
// Creamos el proceso hijo usando spawn
// spawn es preferible sobre exec cuando:
// - El comando produce mucha salida
// - Necesitas recibir la salida en tiempo real
// - Quieres evitar problemas de buffer overflow
const proceso = spawn(comando, argumentos);
// Variable para acumular toda la salida del comando
let salidaCompleta = "";
// Manejador para el evento 'data' de stdout (salida estándar)
// Este evento se dispara cada vez que el comando produce salida
proceso.stdout.on("data", (data) => {
// Convertimos el buffer de datos a string
const salida = data.toString();
// Acumulamos la salida para retornarla al final
salidaCompleta += salida;
// Mostramos la salida en tiempo real, recortando espacios en blanco
console.log("Salida:", salida.trim());
});
// Manejador para el evento 'data' de stderr (salida de error)
proceso.stderr.on("data", (data) => {
// Mostramos los errores en tiempo real
console.error("Error:", data.toString().trim());
});
// Manejador para el evento 'close' - se ejecuta cuando el proceso termina
proceso.on("close", (codigo) => {
// El código de salida indica si el proceso terminó exitosamente
// Código 0 = éxito, otros códigos = error
console.log(`Proceso terminado con código: ${codigo}`);
// Resolvemos la Promise con toda la salida acumulada
resolve(salidaCompleta);
});
// Manejador para el evento 'error' - se ejecuta si hay error al iniciar el proceso
proceso.on("error", (error) => {
// Esto ocurre si el comando no existe o no se puede ejecutar
console.error("Error en el proceso:", error.message);
// Rechazamos la Promise con el error
reject(error);
});
});
}
// Función asíncrona para demostrar el uso de spawn
async function demostracionSpawn() {
try {
// Ejecutamos el comando 'find' para buscar archivos JavaScript
// Argumentos:
// '.' - directorio actual
// '-name', '*.js' - buscar archivos con extensión .js
// '-type', 'f' - solo archivos regulares (no directorios)
await ejecutarComandoTiempoReal("find", [
".",
"-name",
"*.js",
"-type",
"f",
]);
// Comentado: ejemplo de comando con pipes (solo sistemas Unix/Linux)
// Nota: Los pipes no funcionan directamente con spawn de esta manera
// Para usar pipes se necesita shell: true o ejecutar en shell
// await ejecutarComandoTiempoReal('ps', ['aux', '|', 'grep', 'node']);
} catch (error) {
console.error("Error en spawn:", error.message);
}
}
// Ejecutamos la demostración
demostracionSpawn();
Diferencias clave entre spawn y exec
- Flujo de datos:
spawnmaneja datos en streams (flujo continuo),execlos almacena en buffer y los retorna todos al final. - Argumentos:
spawnrecibe comando y argumentos separados,execrecibe el comando completo como string. - Rendimiento:
spawnes más eficiente para comandos que producen mucha salida. - Uso de shell:
execusa shell por defecto,spawnno (puede activarse con{shell: true}).
Ventajas de spawn en este contexto
- Muestra la salida inmediatamente mientras se genera
- No tiene límites de buffer para la salida
- Más control sobre el proceso hijo
- Mejor para comandos de larga duración o con mucha salida
El comando find utilizado busca recursivamente todos los archivos JavaScript en el directorio actual y sus subdirectorios, mostrando los resultados a medida que los encuentra.
Módulo Worker Threads - Hilos de Trabajo
Para operaciones intensivas de CPU, los worker threads permiten ejecutar código JavaScript en paralelo.
import { Worker, isMainThread, parentPort, workerData } from "worker_threads";
function tareaIntensivaCPU(numero) {
// Simular una tarea que consume CPU
let resultado = 0;
for (let i = 0; i < numero; i++) {
resultado += Math.sqrt(i) * Math.sin(i);
}
return resultado;
}
if (isMainThread) {
// Código del hilo principal
async function ejecutarConWorkerThreads() {
console.log("=== WORKER THREADS DEMOSTRACIÓN ===");
const tareas = [10000000, 20000000, 30000000];
const workers = [];
for (let i = 0; i < tareas.length; i++) {
const worker = new Worker(new URL(import.meta.url), {
workerData: { valor: tareas[i], id: i + 1 },
});
workers.push(
new Promise((resolve, reject) => {
worker.on("message", resolve);
worker.on("error", reject);
worker.on("exit", (code) => {
if (code !== 0) {
reject(new Error(`Worker se detuvo con código ${code}`));
}
});
})
);
}
console.log("Ejecutando tareas intensivas en workers paralelos...");
const inicio = Date.now();
try {
const resultados = await Promise.all(workers);
const duracion = Date.now() - inicio;
console.log(`Todas las tareas completadas en ${duracion}ms`);
resultados.forEach((resultado, index) => {
console.log(`Worker ${index + 1}: ${resultado}`);
});
} catch (error) {
console.error("Error en workers:", error.message);
}
}
ejecutarConWorkerThreads();
} else {
// Código del worker thread
const resultado = tareaIntensivaCPU(workerData.valor);
parentPort.postMessage(`Tarea ${workerData.id} completada: ${resultado}`);
}
Manejo de Señales del Sistema
Además de la información básica que ofrece el objeto process, Node.js permite reaccionar a ciertos eventos generados por el propio sistema operativo. Estos eventos se conocen como señales. Una de las más habituales es SIGINT, que se envía cuando el usuario pulsa Control + C para intentar detener un programa en ejecución.
Trabajar con señales permite cerrar el programa de forma ordenada, liberar recursos, guardar datos pendientes o completar una operación crítica antes de finalizar.
function configurarManejoSeñales() {
console.log("=== MANEJO DE SEÑALES DEL SISTEMA ===");
console.log("Proceso ID:", process.pid);
console.log("Envía señales con: kill -SEÑAL", process.pid);
// Manejar señal de interrupción (Ctrl+C)
process.on("SIGINT", () => {
console.log("\\nRecibida señal SIGINT (Ctrl+C). Cerrando limpiamente...");
// Realizar limpieza antes de salir
setTimeout(() => {
console.log("Limpieza completada. Saliendo.");
process.exit(0);
}, 1000);
});
// Manejar señal de terminación
process.on("SIGTERM", () => {
console.log("Recibida señal SIGTERM. Realizando shutdown graceful...");
// Cerrar conexiones, guardar estado, etc.
process.exit(0);
});
// Manejar cierre inesperado
process.on("uncaughtException", (error) => {
console.error("Error no capturado:", error);
// En producción, podrías registrar el error y continuar
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Promise rechazada no manejada:", reason);
// En producción, registrar y posiblemente continuar
});
// Mantener el proceso activo
console.log("Proceso ejecutándose. Presiona Ctrl+C para terminar.");
}
configurarManejoSeñales();
Ejemplo Práctico: Monitor del Sistema
Vamos a crear un monitor completo del sistema que combine todos los conceptos:
import os from "os";
import { exec } from "child_process";
class MonitorSistema {
constructor() {
this.datosHistoricos = [];
this.intervaloMonitoreo = null;
}
obtenerUsoCPU() {
const cpus = os.cpus();
let totalIdle = 0;
let totalTick = 0;
cpus.forEach((cpu) => {
for (const type in cpu.times) {
totalTick += cpu.times[type];
}
totalIdle += cpu.times.idle;
});
return {
total: totalTick,
idle: totalIdle,
uso: 100 - Math.round((100 * totalIdle) / totalTick),
};
}
obtenerEstadoMemoria() {
const total = os.totalmem();
const libre = os.freemem();
const usado = total - libre;
return {
total: Math.round(total / 1024 / 1024),
libre: Math.round(libre / 1024 / 1024),
usado: Math.round(usado / 1024 / 1024),
porcentaje: Math.round((usado / total) * 100),
};
}
obtenerEstadoSistema() {
const memoria = this.obtenerEstadoMemoria();
const cpu = this.obtenerUsoCPU();
return {
timestamp: new Date().toISOString(),
plataforma: os.platform(),
tiempoActividad: Math.round(os.uptime() / 3600),
memoria: memoria,
cpu: cpu,
cargaSistema: os.loadavg(),
procesosNode: {
memoria: Math.round(process.memoryUsage().rss / 1024 / 1024),
pid: process.pid,
},
};
}
iniciarMonitoreo(intervalo = 5000) {
console.log("Iniciando monitor del sistema...");
console.log("Intervalo de monitoreo:", intervalo + "ms");
this.intervaloMonitoreo = setInterval(() => {
const estado = this.obtenerEstadoSistema();
this.datosHistoricos.push(estado);
// Mantener solo los últimos 100 registros
if (this.datosHistoricos.length > 100) {
this.datosHistoricos.shift();
}
this.mostrarEstadoActual(estado);
}, intervalo);
}
detenerMonitoreo() {
if (this.intervaloMonitoreo) {
clearInterval(this.intervaloMonitoreo);
console.log("Monitor detenido");
}
}
mostrarEstadoActual(estado) {
console.log("\\n--- ESTADO DEL SISTEMA ---");
console.log("Hora:", new Date().toLocaleTimeString());
console.log(
`Memoria: ${estado.memoria.usado}MB / ${estado.memoria.total}MB (${estado.memoria.porcentaje}%)`
);
console.log(`CPU: ${estado.cpu.uso}% de uso`);
console.log(
`Carga del sistema: ${estado.cargaSistema[0].toFixed(2)} (1min)`
);
console.log(`Proceso Node: ${estado.procesosNode.memoria}MB RSS`);
}
generarReporte() {
if (this.datosHistoricos.length === 0) {
console.log("No hay datos de monitoreo disponibles");
return;
}
const usoCPUPromedio =
this.datosHistoricos.reduce((sum, dato) => sum + dato.cpu.uso, 0) /
this.datosHistoricos.length;
const memoriaPromedio =
this.datosHistoricos.reduce(
(sum, dato) => sum + dato.memoria.porcentaje,
0
) / this.datosHistoricos.length;
console.log("\\n=== REPORTE DE MONITOREO ===");
console.log(
"Período de monitoreo:",
this.datosHistoricos.length,
"muestras"
);
console.log("Uso de CPU promedio:", usoCPUPromedio.toFixed(2) + "%");
console.log("Uso de memoria promedio:", memoriaPromedio.toFixed(2) + "%");
console.log(
"Tiempo máximo de actividad:",
this.datosHistoricos[this.datosHistoricos.length - 1].tiempoActividad +
" horas"
);
}
ejecutarComandoPersonalizado(comando) {
return new Promise((resolve, reject) => {
exec(comando, { encoding: "utf8" }, (error, stdout, stderr) => {
if (error) {
reject(error);
return;
}
resolve(stdout || stderr);
});
});
}
}
// Uso del monitor
async function demostracionMonitor() {
const monitor = new MonitorSistema();
// Configurar manejo de señales
process.on("SIGINT", () => {
console.log("\\nDeteniendo monitor...");
monitor.detenerMonitoreo();
monitor.generarReporte();
process.exit(0);
});
// Iniciar monitoreo
monitor.iniciarMonitoreo(3000);
// Ejecutar algunos comandos después de 15 segundos
setTimeout(async () => {
try {
console.log("\\n--- EJECUTANDO COMANDOS DEL SISTEMA ---");
const espacioDisco = await monitor.ejecutarComandoPersonalizado("df -h");
console.log("Espacio en disco:");
console.log(espacioDisco);
} catch (error) {
console.error("Error al ejecutar comando:", error.message);
}
}, 15000);
// Detener automáticamente después de 30 segundos para la demostración
setTimeout(() => {
console.log("\\n--- DEMOSTRACIÓN COMPLETADA ---");
monitor.detenerMonitoreo();
monitor.generarReporte();
process.exit(0);
}, 30000);
}
// Ejecutar la demostración
// demostracionMonitor();
Gestión de Procesos en Producción
import cluster from "cluster";
import os from "os";
if (cluster.isPrimary) {
console.log("=== CLUSTER MODE - PROCESS MANAGER ===");
console.log("Proceso principal PID:", process.pid);
console.log("CPUs disponibles:", os.cpus().length);
// Crear un worker por CPU
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Manejar eventos de workers
cluster.on("online", (worker) => {
console.log(`Worker ${worker.process.pid} iniciado`);
});
cluster.on("exit", (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} terminado`);
console.log("Iniciando nuevo worker...");
cluster.fork();
});
// Manejar señales para shutdown graceful
process.on("SIGTERM", () => {
console.log("Recibido SIGTERM, cerrando workers...");
for (const id in cluster.workers) {
cluster.workers[id].kill("SIGTERM");
}
});
} else {
// Código de los workers
console.log(`Worker ${process.pid} ejecutándose`);
// Simular una aplicación web/API
const http = await import("http");
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(`Respuesta desde worker ${process.pid}\\n`);
});
server.listen(3000, () => {
console.log(`Worker ${process.pid} escuchando en puerto 3000`);
});
}
Conclusión
El manejo de procesos en Node.js con ES Modules proporciona capacidades completas para:
- Monitorear y controlar el proceso actual de Node.js
- Obtener información detallada del sistema operativo
- Ejecutar comandos del sistema y otros programas
- Aprovechar múltiples núcleos de CPU con worker threads
- Crear aplicaciones en cluster para producción
- Manejar señales del sistema para shutdown graceful
Estas capacidades son esenciales para construir aplicaciones robustas que necesitan interactuar con el sistema operativo, gestionar recursos eficientemente y escalar en entornos de producción.