Getting more out of javascript console messages

Table of contents

One of the first things you do when learning a new language is printing some text to console, so you are likely already familiar with console.log(). But the console package contains a bunch of other functions that can make your output and debugging experience a lot easier if used correctly. That's what we are exploring today!

Log levels

Like any self-respecting logging library, the console package gives us a way to define the urgency of our log messages:

console.debug("for debugging information")
console.log("for normal log messages")
console.info("for informational messages or context")
console.warn("for non-critical warnings")
console.error("for something that went very wrong")

In addition to providing more distinction between outputs, browsers will also let you filter log contents based on log level, so quickly checking through important events by setting filters to warnings and errors only will add to your productivity.

Customizing the log

If you want to get rid of previous log messages, console.clear() has got you covered. The logging functions above also support custom styling. Custom css styles can be applied to log messages using the %c formatting directive.

console.log("%cI am super urgent!", "color:red")

Almost any css style is valid, so you could even use this to heavily modify the appearance of your log messages:

console.log(
	"%c%s%c%s",
	"background:mediumturquoise; color:white; padding:4px 8px; margin-right:2px; border-radius:3px; display:inline-block",
	"Main thread",
	"color:mediumturquoise; display:inline-block; padding-top:4px", "Work has started in the main thread"
)
console.error(
	"%c%s%c%s",
	"background:mediumvioletred; color:white; padding:4px 8px; margin-right:2px; border-radius:3px; display:inline-block",
	"Main thread",
	"color:mediumvioletred; display:inline-block; padding-top:4px", "Something went seriously wrong!"
)

Will produce output similar to this:

Inspecting data

Often times console messages are used to check to current contents or structure of variable contents. While console.log() may be your friend in most cases, it can have undesirable behaviour: Depending on your browser and version, console.log() may print the toString() representation of objects or html-like trees for DOM nodes.

To get a consistent view of the javascript object including variables and methods, you can use the console.dir() function:

If you are trying to check an array, it is usually nicer to format it as a table using console.table() instead:

Grouping messages

Grouping is a way to create visual output hierarchies through indentation. It works by calling console.group(), after which all console outputs are shifted one level to the right. Leave the current group by calling console.groupEnd():

console.log("I am on the base level")
console.group("My Grouped output")
console.log("I am shifted to the right")
console.error("I am also part of the group")
console.groupEnd()
console.info("This message is not grouped anymore")

Which results in:

You can also nest groups into each other:

console.group("First group")
console.log("I am in the first group")
console.log("I am also in the first group")
console.group("Second group")
console.log("I am one level deeper!")
console.groupEnd()
console.log("Back in the first group")
console.groupEnd()
console.log("I am outside of any groups")

This makes the grouped messages easy to scan:

Tracing function calls

While simply writing values to console is enough for simple debugging situations, real world applications quickly become more complex and figuring out where output came from can be tedious. This is what console.trace() is for: it not only prints the message given to it, but also includes a stack trace of where it was called from (and what lead it that being called). Consider a message coming from a deeply nested function:

function deeply(val){
   function nested(val){
      function call(val){
         console.log("\n\nOnly console.log: ", val)
         console.trace("\n\nWith tracing: ", val)
      }
      call(val)
   }
   nested(val)
}
deeply("hi")

The difference is immediately obvious:

Timers & Counters

If you want to keep track of something from a loop or from nested functions, console.count() is quite helpful:

for(let i = 0; i <10; i++){
	console.count("Loop ran")
}

This will log a message with an increasing counter every time the counting function is executed:

If you are interested in timing the speed of something instead, console.time() has 3 built-in commands ready: console.time("My timer") will start a timer called "My Timer", console.timeEnd("My Timer") will end that timer again and print the total amount of time elapsed since starting it. If you want to print intermediate time values without ending the timer, use console.timeLog("My Timer"):

console.time("My timer")
for(let i = 0; i <1000000; i++){
    if(i == 500000){
        console.timeLog("My timer")
    }
}
console.timeEnd("My timer")

This would print timing information to the console:

And just like that, you have profiled the execution speed of your code.

More articles

Handling javascript exceptions

Mistakes happen, here is how you deal with them

Resetting Windows passwords from linux

Regain control of your windows 7/8/10/11 pc with just a few commands

Understanding how LFI/RFI exploits work

Exploring the vulnerabilities in a demo application

Working with tar archives

...and tar.gz, tar.bz2 and tar.xz

Understanding the linux tee command

Copying stdin to multiple outputs

Protecting linux servers from malware with ClamAV and rkhunter

Finding malicious files the open source way