We're live-coding on Twitch! Join us!

Introduction

In this article, we'll learn how to use most methods available in the nodeJS console class more effectively.

To demonstrate, I'll use Chrome browser version 70.0.3538.77 and nodeJS version 8.11.** 3**.

Okay then. Let's begin.

console.log, console.info and console.debug

console.log(string, substitution)

While the famed console.log method really needs no introduction, you'll be glad to know that the console.info and console.debug methods are identical to console.log in their operation.

You can use console.debug in the Firefox browser console by default but to use it in Chrome, you'll have to set the log level to verbose like this.

The console.log method prints to standard out, whether this be the terminal or browser console. It outputs strings by default but can be used in conjuction with template strings to modify what it returns.

Here's how it works:

The arguments in the template string are passed to util.format which then processes the arguments by replacing each substitution token with the respective converted value. The supported substitution tokens are:

%s

const msg = `Using the console class`;
console.log('%s', msg); // Using the console class
console.log(msg); // Using the console class

%s is the default substitution pattern.

%d, %f, %i, %o

const circle = (radius = 1) => {
  const profile = {};
  const pi = 22/7;
  profile.diameter = 2_pi_radius;
  profile.circumference = pi_radius_2;
  profile.area = pi_radius^2;
  profile.volume = 4/3_pi_radius^3;

  console.log('This circle has a radius of: %d cm', radius);
  console.log('This circle has a circumference of: %f cm', profile.diameter);
  console.log('This circle has an area of: %i cm^2', profile.area);
  console.log('The profile of this cirlce is: %o', profile);
  console.log('Diameter %d, Area: %f, Circumference %i', profile.diameter, profile.area, profile.circumference)
}

circle();

This is what happens:

%d will be substituted by a digit (integer or float). %f will be replaced by a float value. %i will be replaced by an integer. %o will be replaced by an Object.

%o is especially handy because we don't have to use JSON.stringify to expand our object because it shows all the object's properties by default.

Note that you can use as many token substitutions as you like. They'll just be replaced the same order as the arguments you pass.

%c

This substitution token applies css styles to the subsituted text.

console.log('LOG LEVEL: %c OK', 'color: green; font-weight: normal');
console.log('LOG LEVEL: %c PRIORITY', 'color: blue; font-weight: medium');

console.log('LOG LEVEL: %c WARN', 'color: red; font-weight: bold');
console.log('ERROR HERE');

Here it is in action.

Above, we note that the text we pass to console.log after the %c substitution token is affected by the styles, but the text before is left as is without styling.

console.table

The first argument passed to it is the data to be returned in the form of a table. The second is an array of selected columns to be displayed.

console.table(tabularData, [properties])

This method will print the input passed to it formatted as a table then log the input object after the table representation.

Arrays

If an array is passed to it as data, each element in the array will be a row in the table.

const books = ['The Silmarillion', 'The Hobbit', 'Unfinished Tales'];
console.table(books);

With a simple array with a depth of 1, the first column in the table has the heading index. Under the index header in the first column are the array indexes and the items in the array are listed in the second column under the value **header.**

This is what happens for a nested array.

const authorsAndBooks = [['Tolkien', 'Lord of The Rings'],['Rutger', 'Utopia For Realists'], ['Sinek', 'Leaders Eat Last'], ['Eyal', 'Habit']];
console.table(authorsAndBooks);

Objects

For objects with a depth of 1, the object keys will be listed under the index header and the values in the object under the second column header.

const inventory = { apples: 200, mangoes: 50, avocados: 300, kiwis: 50 };
console.table(inventory);

For nested objects,

const forexConverter = { asia: { rupee: 1.39, renminbi: 14.59 , ringgit: 24.26 }, africa: { rand: 6.49, nakfa: 6.7 , kwanza:0.33 }, europe: { swissfranc: 101.60, gbp: 130, euro: 115.73 } };
console.table(forexConverter);

Some more nested objects,

const workoutLog = { Monday: { push: 'Incline Bench Press', pull: 'Deadlift'}, Wednesday: { push: 'Weighted Dips', pull: 'Barbell Rows'}};
console.table(workoutLog);

Here, we specify that we only want to see data under the column push.

console.table(workoutLog, 'push');

To sort the data under a column, just click the column header.

Pretty handy, eh?

Try passing console.table an object with some values as arrays!

console.dir

The first argument passed to this function is the object to be logged while the second is an object containing options that will define how the resulting output is fomatted or what properties in the object will be shown.

What's returned is an object formatted by node's util.inspect function.

Nested or child objects within the input object are expandable under disclosure triangles.

console.dir(object, options);
// where options = { showHidden: true ... }

Let's see this in action.

const user = {
  details: {
    name: {
      firstName: 'Immanuel',
      lastName: 'Kant'
    },
    height: `1.83m"`,
    weight: '90kg',
    age: '80',
    occupation: 'Philosopher',
    nationality: 'German',
    books: [
      {
        name: 'Critique of Pure Reason',
        pub: '1781',
      },
      {
        name: 'Critique of Judgement',
        pub: '1790',
      },
      {
        name: 'Critique of Practical Reason',
        pub: '1788',
      },
      {
        name: 'Perpetual Peace',
        pub: '1795',
      },
    ],
    death: '1804'
  }
}

console.dir(user);

Here it is in the Chrome console.

console.dirxml

This function will render an interactive tree of the XML/HTML it is passed. It defaults to a Javascript object if it's not possible to render the node tree.

console.dirxml(object|nodeList);

Much like console.*dir, *the rendered tree can be expanded through clicking disclosure triangles within which you can see child nodes.

It's output is similar to that which we find under the Elements tab in the browser.

This is how it looks when we pass in some HTML from a Wikipedia page.

Let's pass in some HTML from a page on this website.

This is how it looks when we pass in an object.

Try calling console.dir on some HTML and see what happens!

console.assert

The first argument passed to the function is a value to test as truthy. All other arguments passed are considered messages to be printed out if the value passed is not evaluated as truthy.

The Node REPL will throw an error halting execution of subsequent code.

console.assert(value, [...messages])

Here's a basic example:

console.assert(false, 'Assertion failed'); // Assertion failed

Now, let's have some fun. We'll build a mini testing framework using console.assert


const sum = (a = 0, b = 0) => Number(a) + Number(b);

function test(functionName, actualFunctionResult, expected) {
  const actual = actualFunctionResult;
  const pass = actual === expected;
  console.assert(pass, `Assertion failed for ${functionName}`);
  return `Test passed ${actual} === ${expected}`;
}

console.log(test('sum', sum(1,1), 2)); // Test passed 2 === 2
console.log(test('sum', sum(), 0)); // Test passed 0 === 0
console.log(test('sum', sum, 2)); // Assertion failed for sum
console.log(test('sum', sum(3,3), 4)); // Assertion failed for sum

Run the above in your node REPL or browser console to see what happens.

console.error & console.warn

These two are essentially identical. They will both print whatever string is passed to them.

However, console.warn prints out a triangle warn symbol before the message passed while console.error prints out a danger symbol before the message passed.

console.error(string, substitution);
console.warn(string, substitution);

Let's note that string substitution can be applied in the same way as the console.log method.

Here's a mini logging function using console.error.

const sum = (a = 0, b = 0) => Number(a) + Number(b);

function otherTest(actualFunctionResult, expected) {
  if (actualFunctionResult !== expected) {
    console.error(new Error(`Test failed ${actualFunctionResult} !== ${expected}`));
  } else {
    // pass
  }
}

otherTest(sum(1,1), 3);

console.trace(label)

This console method will prints the string Trace: followed by the label passed to the function then the stack trace to the current position of the function.

function getCapital(country) {
  const capitalMap = {
    belarus: 'minsk', australia: 'canberra', egypt: 'cairo', georgia: 'tblisi', latvia: 'riga', samoa: 'apia'
  };
  console.trace('Start trace here');
  return Object.keys(capitalMap).find(item => item === country) ? capitalMap[country] : undefined;
}

console.log(getCapital('belarus'));
console.log(getCapital('accra'));

console.count(label)

Count will begin and increment a counter of name label** .**

Let's build a word counter to see how it works.

const getOccurences = (word = 'foolish') => {
  const phrase = `Oh me! Oh life! of the questions of these recurring, Of the endless trains of the faithless, of cities fill’d with the foolish, Of myself forever reproaching myself, for who more foolish than I, and who more faithless?`;

  let count = 0;
  const wordsFromPhraseArray = phrase.replace(/[,.!?]/igm, '').split(' ');
  wordsFromPhraseArray.forEach((element, idx) => {
    if (element === word) {
      count ++;
      console.count(word);
    }
  });
  return count;
}

getOccurences();

Here, we see that the word foolish was logged twice. Once for each appearance of the word in the phrase.

We could use this as a handy method to see how many times a function was called or how many times a line in our code executed.

console.countReset(label)

As the name suggests, this resets a counter having a label set by the console.count method.

const getOccurences = (word = 'foolish') => {
  const phrase = `Oh me! Oh life! of the questions of these recurring, Of the endless trains of the faithless, of cities fill’d with the foolish, Of myself forever reproaching myself, for who more foolish than I, and who more faithless?`;

  let count = 0;
  const wordsFromPhraseArray = phrase.replace(/[,.!?]/igm, '').split(' ');
  wordsFromPhraseArray.forEach((element, idx) => {
    if (element === word) {
      count ++;
      console.count(word);
      console.countReset(word);
    }
  });
  return count;
}

getOccurences();

We can see that our getOccurences function returns 2 because there are indeed two occurences of the word foolish in the phrase but since our counter is reset at every match, it logs foolish: 1twice.

console.time(label) and console.timeEnd(label)

The console.time function starts a timer with the label supplied as an argument to the function, while the console.*timeEnd *function stops a timer with the *label *supplied as an argument to the function_._

console.time('<timer-label>');
console.timeEnd('<timer-label'>);

We can use it to figure out how much time it took to run an operation by passing in the same label name to both functions.

const users = ['Vivaldi', 'Beethoven', 'Ludovico'];

const loop = (array) => {
  array.forEach((element, idx) => {
    console.log(element);
  })
}

const timer = () => {
  console.time('timerLabel');
  loop(users);
  console.timeEnd('timerLabel');
}

timer();

We can see the timer label displayed against time value after the timer is stopped.

It took the loop function 0.6909ms to finish looping through the array.

Conclusion

At last, we've come to the end of this tutorial. I hope you've enjoyed it.

I've left out the non standard uses of the console class like console.profile, console.profileEnd and console.timeLog , but feel free to experiment with them and let me know how it goes.

Feedback

As always, if you have any feedback on the article for me or if you'd like to chat, find me at @mabishi on the Scotch Slack or drop me a line in the comments below.

Like this article? Follow @emabishi on Twitter