Logs Avanzados en la Consola
La consola del navegador ofrece capacidades avanzadas de logging que van mucho más allá del simple console.log(). Estas técnicas avanzadas te permitirán organizar, dar formato y estructurar tus mensajes para obtener información más clara y útil durante el desarrollo y depuración.
Formateo de cadenas
La consola admite un sistema de formato similar a printf para crear mensajes más complejos:
Marcadores de sustitución básicos
// %s - String
console.log('Hola, %s!', 'mundo');
// %i o %d - Integer
console.log('Tienes %i mensajes nuevos', 5);
// %f - Float
console.log('El precio es %f euros', 23.5);
// %o - Objeto expandible
console.log('Datos del usuario: %o', {nombre: 'Ana', edad: 28});
// %O - Objeto con formateo mejorado (más detallado que %o)
console.log('Detalles completos: %O', document.body);
// %c - Aplicar estilos CSS
console.log('%cTexto destacado', 'color: red; font-size: 20px;');
Combinación de múltiples marcadores
console.log(
'Usuario: %s, Edad: %i, Premium: %s',
'Juan',
34,
true
);
Estilos visuales avanzados
La consola permite aplicar estilos CSS mediante el marcador %c:
Texto con estilos
console.log(
'%cÉxito!%c Operación completada correctamente',
'color: white; background: green; padding: 2px 5px; border-radius: 2px; font-weight: bold;',
'color: green; font-style: italic;'
);
Múltiples segmentos con estilo
console.log(
'%cError %cProcesando %cDatos',
'color: white; background: red; padding: 2px 5px; border-radius: 2px;',
'color: black; background: yellow; padding: 2px 5px;',
'color: white; background: blue; padding: 2px 5px;'
);
Fondos con gradientes
console.log(
'%cTítulo importante',
'font-size: 20px; font-weight: bold; background: linear-gradient(to right, #ff8a00, #da1b60); color: white; padding: 10px; border-radius: 4px;'
);
Separadores visuales
console.log(
'%c----------------------------------------',
'color: gray; font-weight: bold;'
);
Emojis e iconos
console.log('✅ Tarea completada');
console.log('⚠️ Advertencia importante');
console.log('❌ Error crítico');
Organización jerárquica de logs
Grupos anidados
console.group('Proceso de autenticación');
console.log('Verificando credenciales...');
console.group('Validación de datos');
console.log('Email: válido');
console.log('Contraseña: correcta');
console.groupEnd();
console.log('Generando token...');
console.log('Autenticación completada');
console.groupEnd();
Grupos contraídos por defecto
console.groupCollapsed('Detalles técnicos (expandir)');
console.log('Información de depuración extensa...');
console.log('Más información técnica...');
console.groupEnd();
Combinación con estilos
console.group('%cResultados del test', 'color: blue; font-weight: bold;');
console.log('Test 1: Pasado');
console.log('Test 2: Pasado');
console.log('%cTest 3: Fallido', 'color: red');
console.groupEnd();
Medición de rendimiento
Cronometraje básico
console.time('operación');
// Código que queremos medir
for (let i = 0; i < 1000000; i++) {
// Operación costosa
}
console.timeEnd('operación'); // Muestra: operación: 123.45ms
Múltiples cronómetros simultáneos
console.time('total');
console.time('parte1');
// Primera operación...
console.timeEnd('parte1');
console.time('parte2');
// Segunda operación...
console.timeEnd('parte2');
console.timeEnd('total');
Puntos intermedios
console.time('proceso');
// Primera fase...
console.timeLog('proceso', 'Primera fase completada');
// Segunda fase...
console.timeLog('proceso', 'Segunda fase completada');
// Fase final...
console.timeEnd('proceso');
Conteo y estadísticas
Contadores básicos
for (const usuario of usuarios) {
procesarDatos(usuario);
console.count('Usuarios procesados');
}
// Muestra incrementalmente:
// Usuarios procesados: 1
// Usuarios procesados: 2
// ...
Contadores con etiquetas
function procesarEvento(tipo) {
console.count(`Eventos de tipo ${tipo}`);
// Procesamiento...
}
procesarEvento('click'); // Eventos de tipo click: 1
procesarEvento('submit'); // Eventos de tipo submit: 1
procesarEvento('click'); // Eventos de tipo click: 2
Reinicio de contadores
console.countReset('Usuarios procesados');
// Reinicia el contador específico a 0
Aserciones y validaciones
Comprobaciones condicionales
const respuesta = await fetch('/api/datos');
console.assert(
respuesta.ok,
'La solicitud API falló con estado:',
respuesta.status
);
El mensaje solo aparece cuando la condición es falsa, sin interrumpir la ejecución.
Validación de datos
function validarUsuario(usuario) {
console.assert(usuario.nombre, 'Nombre de usuario requerido');
console.assert(usuario.edad >= 18, 'El usuario debe ser mayor de edad');
console.assert(
typeof usuario.email === 'string' && usuario.email.includes('@'),
'Email inválido'
);
}
Trazas y stack
Trazas de ejecución
function funcionA() { funcionB(); }
function funcionB() { funcionC(); }
function funcionC() { console.trace('Traza de ejecución'); }
funcionA();
// Muestra la pila de llamadas completa hasta funcionC
Trazas personalizadas
console.trace('%cPunto de control importante', 'color: orange');
Tablas complejas
Datos multidimensionales
const usuarios = [
{id: 1, nombre: 'Ana', rol: 'Admin', departamentos: ['IT', 'RRHH']},
{id: 2, nombre: 'Juan', rol: 'Editor', departamentos: ['Marketing']},
{id: 3, nombre: 'Elena', rol: 'Usuario', departamentos: ['Ventas', 'Soporte']}
];
console.table(usuarios);
Selección de columnas
console.table(usuarios, ['nombre', 'rol']);
Datos indexados personalizados
const usuariosPorId = {
'usr_123': {nombre: 'Ana', conectado: true},
'usr_456': {nombre: 'Luis', conectado: false},
'usr_789': {nombre: 'Elena', conectado: true}
};
console.table(usuariosPorId);
Contexto y estado
Captura de contexto
function logContexto(mensaje, contexto = {}) {
console.groupCollapsed(`${mensaje} - ${new Date().toLocaleTimeString()}`);
if (Object.keys(contexto).length) {
console.log('Contexto:', contexto);
}
console.log('URL:', window.location.href);
console.log('Usuario:', getCurrentUser());
console.trace('Origen');
console.groupEnd();
}
// Uso
logContexto('Error al procesar pago', {
idTransaccion: 'TX12345',
monto: 150.75,
error: 'Fondos insuficientes'
});
Datos de depuración ricos
class Logger {
static info(mensaje, datos) {
this._log('info', mensaje, datos);
}
static error(mensaje, error, datos) {
this._log('error', mensaje, {
...datos,
errorNombre: error.name,
errorMensaje: error.message,
stack: error.stack
});
}
static _log(nivel, mensaje, datos = {}) {
const estilos = {
info: 'color: blue; font-weight: bold;',
error: 'color: red; font-weight: bold;'
};
console.groupCollapsed(
`%c${nivel.toUpperCase()}: ${mensaje}`,
estilos[nivel]
);
if (datos) console.log('Datos:', datos);
console.trace('Traza');
console.groupEnd();
}
}
// Uso
try {
// Código que puede fallar
throw new Error('Error al procesar datos');
} catch (error) {
Logger.error('Fallo en la operación', error, {
operacion: 'procesarPago',
idUsuario: 12345
});
}
Dir y dirxml para objetos especiales
Exploración estructural
// Ver propiedades completas, incluyendo no enumerables
console.dir(document.body);
// Ver como árbol DOM (similar a la vista Elements)
console.dirxml(document.body);
Visualización profunda
class ComponenteComplejo {
constructor() {
this.estado = { /* ... */ };
this.props = { /* ... */ };
// Propiedades privadas
this._cache = new Map();
this._listeners = [];
}
}
const componente = new ComponenteComplejo();
console.dir(componente, { depth: null }); // Muestra todos los niveles anidados
Exportación y compartición de logs
Copiar al portapapeles
const datos = [/* datos complejos */];
console.log('Datos para análisis:', datos);
// En la consola:
// Click derecho > "Store as global variable"
// Obtiene temp1
copy(temp1); // Copia al portapapeles
URLs de depuración
console.log(
'Para reproducir: %cclip%c',
'color: blue; text-decoration: underline; cursor: pointer;',
'',
{
url: window.location.href,
params: new URLSearchParams(window.location.search).toString(),
estado: obtenerEstadoActual()
}
);
Logging condicional y personalizado
Niveles de log configurables
const LogLevel = {
ERROR: 0,
WARN: 1,
INFO: 2,
DEBUG: 3,
TRACE: 4
};
class LoggerAvanzado {
constructor(nivel = LogLevel.INFO) {
this.nivel = nivel;
}
setNivel(nivel) {
this.nivel = nivel;
}
error(...args) {
if (this.nivel >= LogLevel.ERROR) {
console.error(...args);
}
}
warn(...args) {
if (this.nivel >= LogLevel.WARN) {
console.warn(...args);
}
}
info(...args) {
if (this.nivel >= LogLevel.INFO) {
console.info(...args);
}
}
debug(...args) {
if (this.nivel >= LogLevel.DEBUG) {
console.debug(...args);
}
}
trace(...args) {
if (this.nivel >= LogLevel.TRACE) {
console.trace(...args);
}
}
}
// Uso
const logger = new LoggerAvanzado();
// En desarrollo
if (process.env.NODE_ENV === 'development') {
logger.setNivel(LogLevel.DEBUG);
}
logger.debug('Información detallada solo visible en desarrollo');
Logging contextual
function crearLoggerContextual(componente) {
return {
log: (...args) => console.log(`[${componente}]`, ...args),
warn: (...args) => console.warn(`[${componente}]`, ...args),
error: (...args) => console.error(`[${componente}]`, ...args),
info: (...args) => console.info(`[${componente}]`, ...args),
debug: (...args) => console.debug(`[${componente}]`, ...args)
};
}
// Uso
const loggerAuth = crearLoggerContextual('Autenticación');
loggerAuth.log('Usuario iniciando sesión');
loggerAuth.error('Error de autenticación', new Error('Credenciales inválidas'));
Integración con errores de la aplicación
Captura global de errores
window.addEventListener('error', (evento) => {
console.group('%cError no capturado', 'color: red; font-weight: bold;');
console.error('Mensaje:', evento.message);
console.error('Origen:', evento.filename);
console.error('Línea:', evento.lineno, 'Columna:', evento.colno);
console.error('Error:', evento.error);
console.groupEnd();
// Opcional: enviar a servicio de monitoreo
});
Captura de promesas rechazadas
window.addEventListener('unhandledrejection', (evento) => {
console.group('%cPromesa rechazada no capturada', 'color: #ff6b00; font-weight: bold;');
console.error('Razón:', evento.reason);
console.trace('Origen');
console.groupEnd();
});
Técnicas avanzadas para desarrollo
Hooks de depuración
// Agregar un hook antes de una función para depurar
const funcionOriginal = miObjeto.funcionImportante;
miObjeto.funcionImportante = function(...args) {
console.group('Llamada a funcionImportante');
console.log('Argumentos:', ...args);
console.time('Duración');
const resultado = funcionOriginal.apply(this, args);
console.log('Resultado:', resultado);
console.timeEnd('Duración');
console.groupEnd();
return resultado;
};
Live expressions
En Chrome DevTools, puedes usar "Create live expression" (ojo + símbolo) para monitorear valores en tiempo real:
// Expresión de monitoreo constante
// document.querySelectorAll('.item').length
// miAplicacion.estado.usuarios.length
// performance.now()
Mejores prácticas para logging productivo
Prevención de fugas de información
// Evitar logging de información sensible
function logUsuario(usuario) {
console.log('Usuario:', {
id: usuario.id,
nombre: usuario.nombre,
// NO incluir: contraseña, tokens, datos personales
rol: usuario.rol,
ultimoAcceso: usuario.ultimoAcceso
});
}
Desactivación condicional
// Solo activar logs detallados en desarrollo
if (process.env.NODE_ENV !== 'production') {
window.DEBUG = true;
}
function logDebug(...args) {
if (window.DEBUG) {
console.debug(...args);
}
}
// Uso
logDebug('Información detallada de depuración', datos);
Exportación a servicios externos
class LoggerRemoto {
constructor(endpoint, nivelMinimo = 'error') {
this.endpoint = endpoint;
this.niveles = ['debug', 'info', 'warn', 'error'];
this.nivelMinimo = this.niveles.indexOf(nivelMinimo);
}
log(nivel, mensaje, datos = {}) {
const nivelIndice = this.niveles.indexOf(nivel);
// Solo enviar logs del nivel configurado o superior
if (nivelIndice >= this.nivelMinimo) {
// Log local para desarrollo
console[nivel](mensaje, datos);
// Envío a servicio remoto
fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
nivel,
mensaje,
datos,
timestamp: new Date().toISOString(),
url: window.location.href,
userAgent: navigator.userAgent
})
}).catch(e => console.error('Error enviando log remoto:', e));
}
}
// Métodos de conveniencia
debug(msg, datos) { this.log('debug', msg, datos); }
info(msg, datos) { this.log('info', msg, datos); }
warn(msg, datos) { this.log('warn', msg, datos); }
error(msg, datos) { this.log('error', msg, datos); }
}
// Uso
const logger = new LoggerRemoto('/api/logs', 'warn');
logger.error('Error crítico', { codigo: 500, detalles: '...' });
Conclusión
Las técnicas avanzadas de logging en la consola del navegador permiten:
- Crear sistemas de logs estructurados y visualmente útiles
- Facilitar la detección y diagnóstico de problemas
- Medir y optimizar el rendimiento de aplicaciones
- Crear sistemas de logs adaptados a las necesidades de cada proyecto
La combinación de estas técnicas con otros paneles de las herramientas de desarrollo proporciona una base sólida para el desarrollo y mantenimiento de aplicaciones web de alta calidad.