wandres.dev
CONSOLE I · Más que console.log

Formato: especificadores, estilos con %c y sus límites

Los seis especificadores de formato de la consola, cómo se aplican los estilos CSS, y para qué sirve de verdad poder pintar la salida.

⏱ 13 min

La consola acepta cadenas de formato al estilo de printf, heredadas de una tradición que llega desde C y que en el navegador tienen un añadido propio: un especificador que aplica CSS al texto. Suena a decoración y tiene tres usos serios —jerarquizar visualmente, marcar categorías, y hacer visible una traza que de otro modo se pierde— además de un puñado de límites que hay que conocer para no perder tiempo peleándose con ellos.

🎯 Al terminar esta lección sabrás
  • Usar los seis especificadores de formato con el tipo de dato correcto.
  • Aplicar estilos con %c y saber hasta dónde llega su alcance.
  • Construir salida categorizada legible para instrumentación permanente.
  • Reconocer los cuatro límites del formato de consola y sus rodeos.

Los especificadores

Cuando el primer argumento de una función de consola es una cadena que contiene especificadores, los argumentos siguientes se consumen en orden para sustituirlos.

%s sustituye por el argumento convertido a cadena.

%d y %i sustituyen por el argumento convertido a entero. Un 3.7 se muestra como 3.

%f sustituye por el argumento convertido a número en coma flotante.

%o sustituye por el argumento formateado como elemento del DOM si lo es, o como objeto si no. Es el formato “óptimo” para el tipo.

%O con mayúscula sustituye por el argumento formateado siempre como objeto JavaScript, con sus propiedades. Es a %o lo que dir es a log.

%c no sustituye por nada: consume el argumento como una cadena de CSS y la aplica al texto que venga después.

const usuario = { id: 42, nombre: 'Ana', roles: ['admin'] };
console.log('Usuario %s con id %d — objeto: %O', usuario.nombre, usuario.id, usuario);

Los argumentos que sobran después de consumir todos los especificadores se muestran a continuación, separados por espacio, con el formato normal.

⚠️
Cuidado

El formato solo se interpreta si el primer argumento es una cadena. console.log(objeto, 'texto con %s', valor) no formatea nada: muestra los tres argumentos tal cual. Es la causa número uno de “los especificadores no funcionan”.

Estilos con %c

El argumento que sigue a %c es una cadena de declaraciones CSS que se aplica al texto posterior, hasta el siguiente %c o hasta el final del mensaje.

console.log(
  '%cAPI%c GET /pedidos %c200%c 34 ms',
  'background:#89b4fa;color:#11111b;padding:2px 6px;border-radius:3px;font-weight:600',
  'color:#a6adc8',
  'background:#a6e3a1;color:#11111b;padding:2px 6px;border-radius:3px',
  'color:#a6adc8'
);

Las propiedades que funcionan son las de texto y caja en línea: color, background, font-size, font-weight, font-family, padding, border, border-radius, text-decoration, line-height. Se pueden usar imágenes de fondo, lo que permite meter un logotipo en la consola, cosa que hacen varias librerías y que es exactamente tan útil como parece.

Lo que no funciona: posicionamiento, transformaciones, animaciones, y en general cualquier cosa que requiera que el texto sea una caja independiente. La consola aplica los estilos a un fragmento en línea dentro de su propia interfaz, con restricciones.

Los tres usos serios

Jerarquía visual en instrumentación permanente. Cuando una aplicación emite trazas de varios subsistemas, un distintivo de color por subsistema hace que el ojo separe las categorías sin leer. Con veinte líneas por segundo, esa diferencia es la que hace la salida legible.

const canal = (nombre, color) => (...args) =>
  console.debug(`%c${nombre}`, `background:${color};color:#11111b;padding:1px 5px;border-radius:3px`, ...args);

const red = canal('RED', '#f9e2af');
const estado = canal('ESTADO', '#89b4fa');
const auth = canal('AUTH', '#cba6f7');

red('GET /pedidos', 200);
estado('carrito ->', { items: 3 });
auth('token renovado');

Ese fragmento funciona pegado tal cual y produce tres canales distinguibles. Al ir en nivel debug, no se ven salvo que actives el filtro detallado.

Marcar el mensaje que importa. En una consola con mil líneas, un mensaje con fondo llamativo se encuentra desplazando en vez de leyendo. Para una traza puntual de investigación, poner un %c con fondo magenta y tamaño grande cuesta nada y ahorra búsquedas.

Hacer visible un valor booleano. Un estado que puede ser correcto o incorrecto se lee más rápido por color que por texto, y una función de dos líneas lo resuelve.

const check = (ok, texto) =>
  console.log(`%c${ok ? 'OK' : 'MAL'}%c ${texto}`,
    `background:${ok ? '#a6e3a1' : '#f38ba8'};color:#11111b;padding:1px 6px;border-radius:3px`, '');
check(document.querySelectorAll('img:not([alt])').length === 0, 'todas las imagenes tienen alt');
check(!!document.querySelector('main'), 'existe un landmark main');

Los cuatro límites

Solo se formatea el primer argumento, y tiene que ser una cadena. Ya visto.

El estilo no se hereda entre mensajes. Cada llamada empieza limpia, y dentro de una llamada cada %c sustituye al anterior por completo, no lo extiende. Si quieres que el segundo tramo herede el color del primero, hay que repetirlo.

Los objetos no se pueden estilar. El CSS se aplica al texto; un objeto expandible se muestra con el formato del panel independientemente de lo que digas.

No hay formato para porcentajes ni relleno. No existe un equivalente de %5.2f. Si quieres columnas alineadas, hay que alinear a mano con padStart y usar una fuente monoespaciada mediante %c.

const fila = (a, b) => console.log('%c' + String(a).padEnd(24) + String(b).padStart(8), 'font-family:monospace');
fila('nodos en el documento', document.querySelectorAll('*').length);
fila('hojas de estilo', document.styleSheets.length);
fila('scripts', document.scripts.length);
Cuándo el formato ayuda y cuándo es una forma elegante de perder el tiempo

Hay un fallo de proporción que conviene nombrar porque es fácil caer en él: dedicar quince minutos a que la salida de depuración quede bonita es quince minutos que no has dedicado a depurar. El formato con %c tiene una capacidad de absorción sorprendente —siempre se puede afinar un poco más el color, el padding, el separador— y esa satisfacción de artesanía se confunde con progreso. La regla que distingue los dos casos es de duración y de audiencia. Si la traza es efímera —la vas a mirar durante los próximos diez minutos y luego la borras— el formato no aporta nada y el tiempo invertido es puro coste; una traza de investigación debe escribirse en tres segundos y no merece ni un color. Si la traza es permanente y compartida —instrumentación que se queda en el código, que va a leer otra persona, que se va a mirar dentro de seis meses cuando algo falle— entonces el formato sí paga, porque la legibilidad se multiplica por cada vez que alguien la lee. La misma lógica se aplica a decidir entre una traza y un logpoint: para lo efímero, logpoint, porque no toca el código y se quita solo; para lo permanente, código de verdad, con nivel debug, con canal de color y con grupo. Confundir las dos categorías produce los dos errores simétricos que se ven en cualquier repositorio: código lleno de console.log de investigación que alguien olvidó borrar, y sesiones de depuración donde se construye un sistema de logging precioso para un bug que se arregla en veinte minutos.