Portada de UrsaKit para una guía sobre métodos de consola en JavaScript

Casi toda depuración empieza con console.log y termina ahí, lo cual es una lástima: la consola tiene una docena de métodos y la diferencia entre ellos es la diferencia entre leer un muro de texto y leer una respuesta. Esta guía cubre los métodos que merece la pena conocer, el comportamiento de referencia frente a copia que hace que los logs parezcan incorrectos y cómo leer un stack trace cuando una llamada asíncrona falla en silencio.

Todo esto se ejecuta en el ejecutor de JavaScript de UrsaKit, que imprime la salida de consola con niveles, agrupación y tiempo de ejecución.

01console.log es solo la primera herramienta

console.log escribe lo que le das, en orden, sin estructura. Está bien para un número y es inútil para una lista de cuarenta registros. El resto de la API existe para añadir estructura:

MétodoÚsalo para
console.logCualquier valor; formateado con %s / %d / %o
console.tableArrays de objetos, renderizados como tabla
console.dirInspeccionar las propiedades de un objeto en lugar de su toString
console.group / console.groupEndAnidar salida relacionada
console.time / console.timeEndMedir el tiempo entre dos puntos
console.countContar cuántas veces se ejecuta una rama
console.assertEscribir solo cuando falla una suposición
console.traceImprimir un stack trace en el punto actual

02Los objetos se registran por referencia

Este es el comportamiento que más tiempo hace perder, porque la consola no miente: muestra el objeto vivo.

const user = { name: 'Ada', plan: 'free' }
console.log(user) // al expandirlo puede mostrar { name: 'Grace', plan: 'pro' }
console.log(user.name) // 'Ada' — un primitivo se captura por valor
user.name = 'Grace'
user.plan = 'pro'

En una consola interactiva la vista del objeto se construye al expandirla, así que refleja el estado final del objeto, no el momento de la llamada. Tres formas de capturar el valor:

console.log(user.name) // registra el primitivo que te importa
console.log(JSON.stringify(user)) // una instantánea en cadena, legible en cualquier consola
console.log(structuredClone(user)) // una copia profunda real, conserva los tipos

Registra el estado que estás comparando, no el objeto entero.

console.log('plan=' + user.plan + ' items=' + items.length) sobrevive a la serialización, se lee igual en un log de servidor y en el navegador, y no puede cambiar por debajo. Reserva el volcado completo para la exploración.

03Agrupar y medir tiempos

La salida relacionada se vuelve legible cuando está anidada, y las duraciones se vuelven comparables cuando se miden en lugar de estimarse.

console.group('checkout')
console.time('validate')

console.count('line') // line: 1
console.count('line') // line: 2

for (let i = 0; i < 1e6; i++) {
  /* work */
}
console.timeEnd('validate') // validate: 12.4ms

console.groupEnd()

Algunos detalles que importan en la práctica:

  • console.time y console.timeEnd deben usar la misma etiqueta, y cada etiqueta debe cerrarse antes de poder abrirse de nuevo.
  • console.count empieza cada etiqueta en cero e imprime el total acumulado, la forma más rápida de demostrar que una función de render se ejecuta dos veces.
  • console.groupCollapsed es igual que console.group pero empieza cerrado, lo que mantiene legible una ejecución larga.

El tiempo medido así incluye todo lo que hay entre las dos llamadas, así que sirve para comparaciones relativas —antes y después de un cambio— más que como benchmark. El ejecutor también informa del tiempo total del fragmento.

04Aserciones y conteo

console.assert es lo contrario de console.log: se queda callado mientras tu suposición se cumple.

function applyDiscount(price, percent) {
  console.assert(
    percent >= 0 && percent <= 100,
    'discount out of range',
    percent
  )
  return price * (1 - percent / 100)
}

applyDiscount(100, 20) // sin salida
applyDiscount(100, 150) // Assertion failed: discount out of range 150

Dentro de un bucle, una aserción es un filtro: solo obtienes salida para los registros que rompen la regla, en lugar de desplazarte por los que no.

05Leer un stack trace

Cuando necesitas saber qué llamante llegó a una función, imprime un trace en lugar de añadir un log a cada candidato:

function parse(input) {
  console.trace('parse called')
  return JSON.parse(input)
}
parse('{"a":1}')
// parse called
//   at parse (<anonymous>:2:11)
//   at <anonymous>:4:1

Para errores lanzados, la pila es el mapa desde el fallo hasta el punto de llamada. Léela de arriba abajo: la primera línea es donde se lanzó el error y las siguientes son sus llamantes. El marco que pertenece a tu código —no a una biblioteca ni al runtime— suele ser el que hay que arreglar.

try {
  JSON.parse('{ "a": 1, }')
} catch (error) {
  console.error(error.name) // SyntaxError
  console.error(error.message) // Expected double-quoted property name in JSON at position 9
  console.error(error.stack) // dónde ocurrió, marco a marco
}

06Errores asíncronos que nunca llegan a un log

Un error lanzado dentro de una función asíncrona se convierte en una promesa rechazada. Si nadie la maneja, el fallo puede ocurrir sin pasar nunca por la línea que estabas mirando.

async function load() {
  throw new Error('network down')
}

load() // rechazo sin manejar: nada lo captura

Dos hábitos hacen visibles estos fallos:

  • Usa await dentro de try/catch, para que el fallo caiga en una línea que controlas.
  • Maneja los rechazos en el límite con .catch() en llamadas sin espera, o escuchando el evento de rechazo del runtime en una página propia.
async function load() {
  throw new Error('network down')
}

try {
  await load()
} catch (error) {
  console.error('load failed:', error.message) // load failed: network down
}

No publiques salida de consola verbosa.

Los logs de depuración en producción filtran estado interno a cualquiera que abra la consola, y registrar objetos grandes en una ruta caliente cuesta tiempo real. Ponlos detrás de un flag o elimínalos antes de compilar.

07Practicar en un entorno aislado

  1. Pega un fragmento y ejecútalo — El ejecutor de JavaScript analiza el código primero e informa de los errores de sintaxis con número de línea.
  2. Lee la salida por niveles — Las líneas de log, warn y error se distinguen, y la consola agrupa la salida igual que el navegador.
  3. Mira el temporizador — La duración informada es el coste real del fragmento, suficiente para comparar dos enfoques.
  4. Prueba la rama que falla — Vuelve a ejecutar con una entrada que rompa la suposición, porque la aserción que nunca disparaste es la que habría cazado el bug.

La consola es una herramienta de medición, no un bloc de notas. Usar el método correcto es lo que convierte una página de salida en una sola línea que responde a la pregunta.

Preguntas frecuentes

¿Por qué console.log muestra un valor incorrecto para mi objeto?

La consola guarda una referencia viva al objeto, no una copia. Si el objeto se modifica después, la vista expandida muestra el estado actual. Registra un primitivo, una cadena JSON o un structuredClone para capturar el valor de ese momento.

¿Qué hace exactamente console.table?

Renderiza un array de objetos como tabla, usando las claves como columnas. Es la forma más rápida de leer una lista de registros y acepta un segundo argumento para limitar las columnas.

¿Cuándo debería usar console.assert?

Cuando solo quieres salida si se incumple una suposición. console.assert(condición, mensaje) no escribe nada mientras la condición sea verdadera, lo que mantiene la consola limpia.

¿Es igual console en Node.js y en el navegador?

En gran medida. table, group, time, count y assert existen en ambos, pero el navegador renderiza objetos interactivos y Node imprime el resultado de inspect, además de eventos de proceso como unhandledRejection.

¿Las llamadas a console ralentizan producción?

Registrar objetos grandes tiene coste, y dejar logs verbosos en producción filtra datos internos a cualquiera que abra la consola. Elimínalos o ponlos detrás de un flag antes de publicar.

¿Cómo veo un stack trace sin lanzar un error?

Llama a console.trace(). Imprime un stack trace en el punto de la llamada, que es justo como descubres cuál de cinco llamantes tiene la culpa.

Volver a los artículos

Artículos relacionados

Herramientas relacionadas