Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

Javascript's console is so much more than just console.log

If you've ever written Javascript for more than just the first few lines, you've very likely learned about console.log. It's a function that prints out its arguments into the console and is very handy for many things. I recently wrote a blog post about why print statements are the best tool for debugging and this blog post is a continuation on that, diving bit deeper into debugging via print in Javascript land.

Why debug via print?

A quick recap for those who didn't read the earlier blog post. Printing is great for a few reasons:

  1. It's fast and straight-forward to write: it'll take only a few seconds to type something like console.log("I AM HERE") and then re-run your app.
  2. It doesn't require any setup: you don't need to set up logging environment or figure out how to plug in your IDE's or editor's debugger into the currently running app.

These two reasons are the main causes for why I always start with print. I'm more likely to get started and I can spend more time and energy figuring out the problem rather than getting annoyed by setting up something more complex.

Say hello to console

When I started programming in Javascript back in the day, console.log was just something you learned early on and it always felt like a single command, kinda like print. Only after a while, I realized and learned that there are two parts: console and log which then opened the door to Narnia for me: there's more to console than just the log function.

In this blog post, we'll take a look at some of the great features and functionalities that exist within this realm.

console.log

Let's start with the most commonly known one, console.log. As said, it prints the arguments into the console. If you're developing a frontend application with Javascript, it'll appear in the console inside the Developer Tools of your browser if you have them open.

console.log('I AM HERE');

Continuing from my example from the previous blog post, the simplest use of the function is to pass it a string or number literal. It's very useful for confirming that we are in the right place.

You can also pass in variables or expressions:

let number = 42;
console.log(number);

console.log(number * 2);

which prints 42 and 84.

Sometimes you want to combine the two and there are a few ways in Javascript to do that:

let name = 'Juhis'l;

console.log('Hello', name, '!');
console.log('Hello ' + name + '!');
console.log(`Hello ${name}!`);

There are minor differences here: the first one prints a whitespace between each argument so it'll come out as Hello Juhis ! where as the other two will print Hello Juhis!

I personally prefer the template literal syntax which is the last of the above example. It makes things easy to read and modify as you don't need to start and end quotes all the time.

console.log with DOM elements and objects

A few special cases come when we want to print out DOM elements and Javascript objects:

const someDiv = document.querySelector('#someDiv');

console.log(someDiv);

When you print out an element with console.log in the browser's console, you'll see an "interactive" DOM representation where you can expand and collapse sections. You can get a JSON object style output if you use console.dir() instead!

For logging objects, you may encounter unexpected results:

let person = {
    name: "Juhis",
    site: "https://hamatti.org",
    favoritePizzaTopping: "pineapple"
}

console.log(person);

This will print what you expect in the beginning but if the underlying object changes, the output will change too as it's a "live" view into the object's state.

To counter this, you can either do the classic JSON stringify-parse trick or create a deep clone with structuredClone:

let person = {
    name: "Juhis",
    site: "https://hamatti.org",
    favoritePizzaTopping: "pineapple"
}

console.log(JSON.parse(JSON.stringify(person)));
// or
console.log(structuredClone(person))

Multiple variables with a nicer output

Sometimes you have a small collection of variables that you want to print out. I've found the best way (meaning, most readable output) to do this is with object property value shorthand:

let name = 'Juhis';
let site = 'https://hamatti.org';
let favoritePizzaTopping = 'pineapple';

console.log({name, site, favoritePizzaTopping})

This creates a temporary object and prints the object out with key-value pairs so you don't need to manually do any additional strings for keys. Very handy!

Style it with CSS!

Another cool feature – that I have used very rarely though – is adding CSS styling with %c identifiers:

console.log('%cJuhis%c is %ccool', 'font-weight: 900', 'font-weight: initial', 'color: green');

Each %c starts a new block and after the string to be printed out, each parameter styles one block.

console.table

Now that we got the basics out of the way, let's get to know some of the friends of the log from the console family, starting with table.

As the name implies, table is for printing tabular data:

let matrix = [[0, 0, 1], [0, 1, 0], [1, 0, 0]];

console.table(matrix);

By providing console.table an iterable, in this case an array of arrays, it'll print out a beautiful table.

You can also provide it an object:

let person = {
    name: "Juhis",
    site: "https://hamatti.org",
    favoritePizzaTopping: "pineapple"
}

console.table(person);

and it'll give you a tabular representation with keys and values.

This is a major improvement over log when looking into bit larger objects, their shape and contents.

If you have an array of objects, you can spesify a second parameter (columns) to provide which properties you want to print out.

let person = {
    name: "Juhis",
    site: "https://hamatti.org",
    favoritePizzaTopping: "pineapple"
}

let person2 = {
    name: 'Definitely human',
    site: 'https://developer.mozilla.org/en-US/docs/Web',
    favoritePizzaTopping: 'web'
}

console.table([person, person2], ['name']);

That comes handy if you have a long list of large objects.

console.count

Sometimes we aren't actually interested in the contents of the data (or there's too much of it) and we just need to know how many times something happens, for example in a complex looping and branching structure.

In that case, we bring in the roalty of the family: console.count.

let fruits = ['apple', 'banana', 'pear', 'orange'];
let vowels = ['a', 'e', 'i', 'o', 'u'];

fruits.forEach(fruit => {
    if(vowels.includes(fruit.charAt(0))) {
       console.count('Starts with a vowel');
	} else {
       console.count('Stars with a consonant');
    }
});

/** prints out: **/
// Starts with a vowel: 1
// Stars with a consonant: 1
// Stars with a consonant: 2
// Starts with a vowel: 2

This is handy for debugging more complex structures as it gives you a ballpark figure to start with: if you think something should happen 50 times but it happens twice or that things should happen roughly 50:50 but the ratio is 1000:1, you know something's wonky.

console.group

Last but not least of the functions I'm introducing in this blog post is console.group. Sometimes following the output can become bit messy if there's a lot of nested stuff. That is often a good indicator that some refactoring might be in order but sometimes in legacy projects you don't have that luxury so you just want to get things debugged.

console.group can be used in combination with the other ones as it only provides a "configuration" as it tells the console to indent following console prints one level. A corresponding function is console.groupEnd that closes the group and returns the indentation back one level.

function catchEmAll(pokemon) {
  console.group('At function body')
  console.log(`Gotta catch 'em all`)
  console.group('Inside loop')
  pokemon.forEach(mon => {
    console.log(`I choose you, ${mon} ${mon} ${mon}!`)
  })
  console.groupEnd()
  console.log(`Function catchEmAll has finished`)
  console.groupEnd()
}

catchEmAll(['Bulbasaur', 'Squirtle', 'Charmander'])
catchEmAll(['Articuno', 'Zapdos', 'Moltres'])

This outputs the following (in Google Chrome, at the time of writing):

A console output with three levels of indentation, group headers bolded.

Get to know the rest of the family!

These are not the only methods in the console object - just the ones I use most often when I'm debugging. To get to know the rest, I recommend checking MDN's doc page for console.

Happy debugging!

Syntax Error

Sign up for Syntax Error, a monthly newsletter that helps developers turn a stressful debugging situation into a joyful exploration.