Learn JavaScript’s console interface
We get so used to writing certain commands in our code that we stop thinking about them. One such command for me is the console.log()
command in JavaScript. I have been using this command forever, along with its close siblings: console.debug()
, console.info()
, console.warn()
and console.error()
; without wondering what else does the console
interface have to offer.
TL; DR
Just read the documentation of the console
interface available here, and go through the list of functions available in the interface.
Logging
Of course, the primary and more popular use of the console
interface is for logging. For the uninitiated, there are different levels of logs that you can use for debugging your JavaScript application. Here is a code example:
console.debug("Hello, Debug!");
console.log("Hello, World!");
console.info("Hello, Info!");
console.warn("Hello, Warning!");
console.error("Hello, Error!");
In NodeJS, everything except warn
and error
would be printed to the STDOUT, while warnings and errors would be printed to STDERR. But you have finer controls if you are running the JavaScript code on a web browser.
If you import this script (named demo.js
in this particular example) in an empty HTML file, here is what you would see in the ‘Console’ of the browser:
On almost every popular browser, you would see an option to change the level of logs that you see. I am using Microsoft Edge for the screenshots in this article. You can see the option at the spot highlighted in the image below.
You can choose the level of logging that you want to see from this dropdown. Here is a mapping of the level of logging to the console
function used:
Verbose:
debug
Info:info
,log
Warnings:warn
Errors:error
and failedassert
s
Please see the following images for reference:
Assertion
They also have a dedicated assert
function to save you from writing an if
block when you just want to make sure some conditions are met, and print an error log if they aren’t.
console.assert(1==2, "Obvious Failed Assertion");
console.assert(1==1, "Obvious Passing Assertion");
The above code produces the following log in the console:
Stack-trace
One of the most useful pieces of information during debugging of a large application is the stack trace. It helps in understanding the flow of information in the application. The console
interface has the trace
function which prints exactly that! Note that warn
and error
functions anyway show their trace on the browser console, but not in NodeJS.
function getObj() {
return {key: "value"};
}const printObj = (obj) => {
console.trace("Printing this object: ", obj);
}const main = () => {
var obj = getObj();
printObj(obj);
}main();
This produces the following output on the browser console:
And this output on the command line console, if you use this in NodeJS:
Group logs
Many times, it might help you better peruse through your logs if they are grouped together logically. You get this outcome using the group()
groupCollapsed()
and groupEnd()
functions. Here is an example that showcases how you can do that across function calls:
function getObj() {
console.log("[getObj] this goes inside the group");
var obj = {key: "value"};
console.groupCollapsed("getObj");
console.table(obj);
console.groupEnd();
return obj;
}const printObj = (obj) => {
console.log("[printObj] this is also in the group itself");
console.warn("Printing this object: ", obj);
}const main = () => {
console.group("obj");
console.log("[main] inside the obj group");
var obj = getObj();
printObj(obj);
console.groupEnd();
}main();
And here is the output of the above on the command line (Note that group
and groupCollapsed
work exactly the same on the command line):
Here is the output of the above on the browser console as it appears by default:
You can choose to expand the logs collapsed under the group which is initially collapsed (the getObj
group, which is instantiated using the groupCollapsed()
method).
Alternatively, you can also collapse the expanded groups and clean up logs that you don’t want to see.
Read Objects better
This is one of the features of console
that didn’t know about, and I realise that it would be super useful! Here are some functions which are specifically created to make it easier to read JavaScript objects:
console.dir()
This function is, in my opinion, useless for the browsers that I use (Google Chrome & Microsoft Edge), but I have read that it is useful for Mozilla Firefox because the console.log()
function does not create a tree structure when objects are printed on its console. Here is some example code:
obj = {
first_name: "Leonel",
last_name: "Messi",
club: "PSG",
jersey_number: 30,
}console.log(obj);
console.dir(obj);
console.log("Some string: ", obj);
console.dir("Some string: ", obj);
Along with the output it produces on Microsoft Edge:
console.table()
This can be very useful if you are working with an object with a lot of keys. This is what the output looks like for the following code:
obj = {
first_name: "Leonel",
last_name: "Messi",
clubs: "PSG",
jersey_number: 30,
}console.log(obj);
console.dir(obj);
console.table(obj);
console.log("Some string: ", obj);
console.dir("Some string: ", obj);
console.table("Some string: ", obj);
Note that the console.table()
function also prints the tree format object as well along with the table. And when passed with another parameter, it behaves exactly like console.log()
.
But I think console.table()
becomes a little less useful for non-shallow objects. Here is how it displays an object that contains arrays and objects:
Code Analysis & Benchmarking
console.count()
In order to understand the flow of the code, especially for iterative and recursive functions, it can be useful to count the number of times the code enters a particular block. In such cases, console
provides an out of the box logging function which also counts the number of times it is called.
It has various features (labels and resetting) which make it easy to count different kinds of events in the code. Here is an example of using it in the recursive factorial function:
function getFactorial(num) {
if ((num % 1 != 0) || (num < 0)) {
console.count("invalid input");
console.error(num + " is not a valid input for `factorial` function");
return;
}
console.count("factorial recursions");
if (num == 0) {
return 1;
}
return num * getFactorial(num-1);
}function factorial(num) {
getFactorial(num);
console.count();
console.countReset("factorial recursions");
}factorial(2);
factorial(2.5);
factorial(-3);
factorial(4);
This produces the following output:
console.time()
It is indispensable to measure the performance of each function and module in your code when you are trying to optimize the performance of your software. It is relevant for both, backend software written in NodeJS as well as for user-interface code written in client-side JavaScript (through any of the various popular frameworks: ReactJS, VueJS, AngularJS etc.). The console
interface provides various methods, namely time()
timeEnd()
and timeLog()
, to help you measure the amount of time between any two checkpoints in your code.
Below is a code example, which measures processing time for one time-log, and time taken for a user action in another.
function getFactorial(num) {
if ((num % 1 != 0) || (num < 0)) {
console.error(num + " is not a valid input for `factorial` function");
return;
}
if (num == 0) {
return 1;
}
return num * getFactorial(num-1);
}function factorial(num) {
console.time("factorial of " + num );
var factorialVal = getFactorial(num);
console.timeLog("factorial of " + num);
alert("Factorial of " + num + " = " + factorialVal);
console.timeEnd("factorial of " + num);
}factorial(2);
factorial(2.5);
factorial(-3);
factorial(4000);
This produces the following output:
I hope reading this article helps you make better use of this native library in JavaScript. So that you write lesser, more readable and easier to debug JavaScript code in the future.