loading...

Like `console.log` But Better

leirasanchez profile image Leira Sánchez ・2 min read

Who hasn't peppered their code with console.logs in an attempt to find that pesky bug? The logs can get daunting and confusing. These will help you enhance your debugging experience with the console.

Did you know the console has more properties than log? Try it yourself! Write this into your console and be amazed.

console.log(console);

I will go through the ones I find the most useful.

console.table();

This method displays your arrays and objects in a neat table. It takes in two parameters, the data, and the names (in an array) of the columns you wish to display (optional). Every element, or property, will correspond to a row in the table.

Example:

const array = [1, 2, 3, 4, 5];
const object = {
  name: "Leira",
  lastName: "Sánchez",
  twitter: "MechEngSanchez",
};

console.log('array: ', array); 
// array:  [ 1, 2, 3, 4, 5 ]

console.log('object: ', object); 
// object:  { name: 'Leira', lastName: 'Sánchez', twitter: 'MechEngSanchez' }

What is displayed when using table, is much more organized and easy to understand.

console.table(array);

console.table(array)

console.table(object);

console.table(object)

console.count()

This method keeps a count of how many times it has been called. I mostly use it to check that my functions are being called when I expect them to. You can provide it with a string as a parameter. It will serve as the label. If left blank, the default label is "default".

let dev = '';
const followMe = (dev) => {
    console.count('followers');
    return `${dev} is following you`;
}

followMe('John'); // followers: 1
followMe('Karen'); // followers: 2
followMe('Camila'); // followers: 3

console.assert()

This method only writes to the console if the assertion is false. You will not see it if it's true. The first parameter is what it will make the check on. The second one is the error message you wish to display.

const sum = (n1, n2) => {
    console.assert(n1 + n2 === 10, 'Not 10');
};

sum(3,2); // Assertion failed: Not 10
sum(5,5); //
sum(10,0); //

Style the console.log

Labels

A quick, easy way to organize and keep track of your console.logs is to add labels. Simply, add a string as the first parameter and whatever you want to log as the second. I also like to add a colon and a space for readability.

const firstName = 'Leira';
console.log('First Name: ', firstName); // First Name: Leira

You can add a string as every other parameter to add multiple labels to multiple values but I find that can get messy fast.

const lastName = 'Sánchez';

console.log('First Name: ', firstName, 'Last Name: ', lastName);
// First Name: Leira Last Name: Sánchez

Messy, right?

Add Flair with CSS!

Make your logs colorful and pretty. Just add '%c' to the front of a string as the first parameter. The second parameter will be the CSS styles as a string.

console.log("%cCLICKED!", "font-family:arial;color:green;font-weight:bold;font-size:3rem");

clicked

Let me know in the comments how else you use these or what other methods you find useful!

Posted on by:

leirasanchez profile

Leira Sánchez

@leirasanchez

Mechanical & Software Engineer 🇵🇷

Discussion

markdown guide
 

Great article, I had never heard on console.assert!

One of the things that I starting doing as a short hand for writing labels is wrapping the variables in an object literal. It saves on the repetitive typing!

const firstName = 'Leira';
const lastName = 'Sánchez';
console.log({firstName, lastName}); // {firstName: "Leira", lastName: "Sánchez"}
 

Wow! This great! I will definitely be using this from now on. Thank you 🙂

 

I like that trick too! And now with console.table you can make it look even better!

 

I like these things because they're just about as simple as console.log but more useful in a lot of cases.

I have a hard time adopting debugging tools any more complicated than print statements.

Great post!

 

Totally agree! Very simple tips that can be used right away.

 

Thanks, Ben.... so much feels here!
It feels so good when someone else acknowledges things like this, really:

"I have a hard time adopting debugging tools any more complicated than print statements"

This 'validates' for those of us who still fumble around semi-blindly with the classic duo of console.log() and alert(stuff) :D

 

It is also possible to use console.log with JSON.stringify (some Object, null, 2)
Example

 

A good use case for JSON.stringify is that in some cases console.log does not print the true value of its arguments.

So wrapping the argument in JSON.stringify prints the actual value.

 

It does print the true value, if you log a variable and then reassign it later the console log will show the original value. However properties of that variable may have changed since the log statement was made and the console only evaluates properties when you expand an object in the console, so you get the current values instead of those at the time you called console.log.

If you're interested in shallow properties you can just log { ...obj } instead of obj, or if you want the entire tree of properties you can use something like Lodash's cloneDeep to get a copy of the object and all nested properties.

npmjs.com/package/lodash.clonedeep

Could you run this code in a web debugger?

var myVar = {a: true};
var myVar2 = {};
myVar2.b = myVar; // {b: {a: true})
myVar.a = false;
console.log(myVar2); // === {b: {a:false}}

What output do you get?

{b: {a:false}}, which is as expected.

Must have been the wrong example.

Try this one.

let data = [1,2,3,4,5,6,7]; // length === 7
console.dir(data); // length === 7. But the actual length is 6.
let obj = data.pop();

The console correctly shows the variable data. As I said, it doesn't enumerate its properties until you click the arrow to expand data, at which point it's correct. It's not necessarily intuitive that it works this way, but it does so because this way because if you try and preserve an exact i.e. deep copy of data you a) start racking up memory costs - from console.dir(window) you can easily go a dozen levels down into nested objects, and b) start having to keep track of circular references to ensure your copying process doesn't get stuck in an infinite loop - imagine adding a line before the console log with data.push(data). Instead when you click the arrow to expand a level it just has to do a shallow copy of the variable you're expanding at that time, no copying or checking for circular references required.

If you want to show data at the point before the pop(), then call console.log([ ...data ]) instead. It still won't show you the previous state if the objects within the array have changed properties, but it will show you all seven items. And if you're dealing with simple objects JSON.stringify is handy, because it only supports simple data types where recursion is impossible.

 

in some cases console.log does not print the true value of its arguments

What's the story there? Any more detail?

It's related to JSON.stringify().
I was programming and the console.log as a source of truth didn't match the expected output.

So I researched my problem and found a snippet. Which mentioned JSON.stringify() in a console.log() call.

Which displays the actual value of an argument passed to it.

Is it to do with this (?):

stackoverflow.com/questions/738906...

If you follow the reference to the example in jsfiddle, you can see the problem:

jsfiddle.net/luken/M6295/

It looks like console.log does a synchronous evaluation of the object when the console is open, but if the console is closed when the call is made, and opened after the call, then it is asynchronous and you get the 'unexpected' values.

I guess I've never seen this because I always have the console open.

Also:

developer.mozilla.org/en-US/docs/W...

"Logging objects
Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).

This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want."

The developer docs from MDN explain it well.

I don't think having the console open made a difference in my case.

Yeah, but seeing it in action helps too - it also helps you see it in your 'dev tools of choice', in case you think it doesn't happen for you.

It's interesting it didn't make any difference for you, but perhaps it was a more normal case of delayed evaluation, like you can get with using objects in a closure (I think the classic example is loop variables used in an event handler that was added inside the loop)?

I recall my scenario was objects and key value assignment.

Seeing it in action would help too.

As I remember it was open and by passing a reference I was presented with different stale key pair values.

Hrm. That's curious. It sounds almost the opposite of how I understand the problem - ie that it would display newer values than you would expect because the evaluation of the object happens later than expected.

If I understand the scenario you describe, it would be something like this:

var myVar = {a: true};
var myVar2 = {};

...

myVar2.b = myVar; // {b: {a: true})

myVar.a = false;

...

console.log(myVar2); //?? got { b: { a: true } } but expected { b: { a: false } )

Or something like that? Perhaps you could try an example to help us see what you mean.

var myVar = {a: true};
var myVar2 = {};
myVar2.b = myVar; // {b: {a: true})
myVar.a = false;
console.log(myVar2); // === {b: {a:false}}

Hrm, yeah. I can't see how that can happen.

I think the main point is that if a developer is working with any data structure and the output scheduled by the .log doesn't seem to be correct then they can employ the use of JSON.parse /.stringify to obtain a source of truth.

Yeah, perhaps. Just being aware that it might not output what you expect is an eye-opener for me - so thanks for making us aware :D

No, I haven't. I looked into them at one point, but found them too 'mind bending'. As I understand it, async/await uses them, so perhaps I have without knowing I was....or perhaps they're not related at all and I've completely mis-remembered.
Why do you ask?

Our conversation seemed like it was going to end. So I proposed a new topic.

I checked online and you may have misunderstood. Generators execute sequentially using yield.

Async/await makes it easy to implement a use case of generators.

Hrm, I don't think I misunderstood. generators came a year before async/await and the latter was a classic follow-on from generators. Of course, I could be mis-remembering, or not putting it correctly...here I found a reference:

"

  1. Async/await makes it easier to implement a particular use case of Generators.
  2. Async function can be decomposed into Generator and promise implementation which are good to know stuff. "

overflowjs.com/posts/Javascript-Ge...

TBH, I never quite got my head around it. Apparently, it is something that came from the Python world, and some of my 'back end' colleagues rave about it. I've never found a need, but perhaps I would if I 'internalised' them better - perhaps I should take a class or something.

I've used generators and they help when you sequentially need to execute a series of asynchronous calls using await. Which is better than using promises which execute promises.then().

So you can yield the value of the await if I remember correctly. And then perform error handling before moving on to the next operation which is part of the series of asynchronous calls.

That's odd/interesting. Is that different than the basic use-case of 'await'? ie

await doThis();
await thenDoThis();
await andFinallyDoThis();

?

Is the difference in the error handling, which you would likely do with a try-catch? Got a snippet to illustrate what you mean?

I used generators with npm redux-sagas/effects

import { call, put } from "redux-sagas/effects";
import Service from "./Sevice";
export const foo* = (action) => {
  const data = yield call(Service.getData, action.userId)
  // do something with the data...
}

Hrm, ok. That doesn't parse to my eyes. I have some code written by a contractor we used that is just as difficult for me to grok now that he's left. I find Redux to be very obtuse. I like the concept on a high level, but the implementation makes my eyes bleed (metaphorically).
Perhaps I'm just too old, or lack the training/knowledge of the more recently languages that such features spring up from (Python is usually to blame, imo). It seems I am required to spend more time learning new things than actually building things :/

Perhaps you can help by explaining what your code snippet actually does - in plain old English, I mean - perhaps with some code that uses foo().

I've put some comments in the snippet I sent you.

I used generators with npm redux-sagas/effects

import { call, put } from "redux-sagas/effects";
// Call requests something
// Put stores something
import Service from "./Sevice";
// Service provides access to API calls
export const foo* = (action) => {
// foo* means it's a generator function
  const data = yield call(Service.getData, action.userId)
  // yield is used to halt execution until a response is provided
  // do something with the data...
  console.log(data.user.last_login_timestamp)
}

You are right though. Developers spend a lot of time learning things. Which it why it's ideal turn on the basics and fundamentals of a particular language that you have interesting using. And then apply a framework to to improve structure and facilitate use cases or business operations or whatever else you need to to implement.

Python is a great language. It run stand-alone as programs from the terminal or in the web or for data science or whatever else you may want to do with it.

And it is hard to understand other people's code sometimes if they don't document clearly.

Code should be understandable just by reading it, if variables and functions are named properly and structured well.

But some developers tend to be quirky and innovative with their coding which can lead to misunderstood implementations or difficult to understand blocks of code. Which leaves the developer working on it with time to learn how it works.

Found an example of the JSON.stringify() when working with console.log() for an Array.

let data = [1,2,3,4,5,6,7]; // length === 7
console.dir(data); // length === 7. But the actual length is 6.
let obj = data.pop();

Look at the length output by the log. And open the property in the log to see the content and look at its length property which should be 6.

let e = [1,2,3,4,5,6,7]; // length === 7
console.dir(JSON.parse(JSON.stringify(e))); // length === 7
let r = e.pop();

So, the calling code does:

var iterator = foo();

var {value, done} = iterator.next(); // call() is executed synchronously and its return value is returned as next().value - next().done is false

iterator.next(); // console.log() executes - this returns {value: undefined, done: true}

IINM, Service.getData just returns a Promise which call() returns, so I'm not sure why you wouldn't simply use async/await:

export const foo = async (action) => {
  const data = await call(Service.getData, action.userId);

  console.log(data.user.last_login_timestamp);
}

I'm thinking all this obfuscation is imposed by the framework you're using, no doubt for good reason, but that's kind of my big beef with such frameworks. I really don't much care for them because they are fragmenting my skillset, not to mention the job market...I no longer am a 'web developer', but am a 'Redux' (or React, or whatever) developer, and the tendency is to forget the underlying platform - I have to essentially throw away what I already know. This means that I am screwed if/when the framework goes away, which they generally do eventually.

I can see value in learning generator functions and iterators, since they are part of the platform and they will surely have a long life. I also value libraries of tools that I can use when I want, but I very much dislike frameworks that swallow everything I do. I am happy to learn things that are likely to become part of the platform.

Re: Python, you've not said anything that isn't also true of javascript, or perl, or any number of scripting languages. I don't have any problem with Python itself, but I dislike that people keep trying to force it, via various tools, into my javascript environment (not to mention the crazy stack dumps it does), when it doesn't really fit and would be much better in javascript in the first place.

frameworklessmovement.org/

It's an implementation of redux-saga. Sagas are implemented using Generator functions.
redux-saga.js.org/docs/basics/Decl...

I've taken onboard that you feel that your skill set is being fragmented and you're no longer a web developer. But isn't part of the journey learning new things. Do you prefer working solely with a language and not using frameworks?

I guess you do because you mentioned that you a developer tends to forget the underlying language implementation, how it works, etc.

Small libraries are very useful. If I remember correctly moment.js is great for handling date formatting and parsing. Due to the irregular nature of the implementations of the Date class.

Re' being screwed if the framework goes away. I looked at your profile and it says that you've mainly been working with JQuery, JS and web technologies. Is that right? What's your go to language for programming on the web.

There's always value in learning something. But in the development domain. That learnt skill needs to be applied. I knew a guy who knew all the buzz words, names of frameworks, some can name it. But he didn't understand basic theory such as data structures and data types.

Re: Re Python, Python is a widely used general-purpose, dynamic, extensible, high-level programming language. ... 'Python is a powerful, opinionated and idiosyncratic (odd) scripting language, loved (and hated) by programmers the world over for its style, syntax and attention to whitespace.

Mad - crazy - stack dumps, not ideal. But I do love a good log.

Why do you feel that Python is being forced? Via various tools? And how is it forced into your JS environment.

when it doesn't really fit and would be much better in javascript in the first place.

What do you mean by this?

you're no longer a web developer

Actually, I don't use frameworks (except when I take on a project from someone else), so I don't feel that :) However, I do feel the pressure to do so - almost every job advertised requires React.

But isn't part of the journey learning new things.

Not really. I want to build things, not learn things just for the sake of it - and it seems like most of these frameworks solve the same sorts of problems in different ways, but you kind of have to choose - well, if you want to be employed anyway.

Do you prefer working solely with a language and not using frameworks?

I consider the platform to be "the framework", and prefer to use that.

I guess you do because you mentioned that you a developer tends to forget the underlying language implementation, how it works, etc.

Yeah, just go to SO and see all the solutions that require jquery...it's not a new problem :)

Why do you feel that Python is being forced? Via various tools? And how is it forced into your JS environment.

It's just lack of thought on the part of some tool developers. Developing a tool in python makes it "less easy" to integrate into the suite of tools I already use which are based on javascript/node. I have direct experience of this...and my (er) 'observations' were accepted and they rewrote the tools in node so they were much easier to integrate into (iirc) my gulpfile (or it might even be earlier than that - gruntfile). Don't get me started on built tools...they're way too complicated too.

That's interesting. How did you contribute?

I've used gulp for build automation in the past. I think it solves the problem of having to do it your self. Or maybe that comment is a no trainer because it's clearly stated on the website.

I'm doing a few things today. One of which is a node app which integrates with Google docs. I need a report taken from a database and stored in a Google Doc which can then make use of GSuite features.

I took a look at frameworklessmovement.org it makes sense to use it in some respects. Web components are really good.

So considering you consider the platform to be a framework. What do you define as a platform?

What do you define as a platform?

The DOM, mostly...also the three languages, html, css and javascript, in that order.

I never thought as the DOM as a platform. But in further notice that makes sense.

Yeah, and it is VERY small and loads really quickly.
Of course, that doesn't apply for node. I'm not sure what the platform is called there...perhaps simply 'node'.

Yeah it's called Node. It provides APIs that allow file system access. You can create servers and more.

Yes, I know what node /is/ - I just don't know what they call the 'platform'.
IE, a browser isn't called 'DOM' though that is (I think) what they call it as a platform.

 

I didn't know this! This is great. I can already think of some uses cases for this. Thank you!

 

Don't forget about console.dir! 🙂

 

I often use console.dir to see the DOM attributes of an element

 
let firstName = "John";
let lastName = "Doe";
console.log('First Name: %s, Last Name: %s', firstName, lastName);
// First Name: John, Last Name: Doe
 

Great post, Leira! I love seeing more people use styles in their logging. I'd like to add one more to your list, console.tap. It's one of my own creation, made to solve the fact that console.log returns undefined. Instead tap returns the first value you passed in. Imagine trying to log the result of the filter:

['1', '2', 'zero' , 3, 4, 5]
    .map(parseNumbers)
    .filter(removeEvens)
    .reduce(( acc, v ) => Math.max(acc, v))
 

This is fascinating and addresses a frequently encountered awkwardness of console.log, thank you! I'm going to start using this a lot

 

I will definitely check it out. Thank you!

 

You forgot the most important console method:

console.group("group name");
console.log("something");
console.groupEnd();

Which creates a collapsible tree for hiding and showing more or less information.

Also console.time and console.timeEnd to track how long something took.

 

I’m going to have to start using groups. They will definitely make my console cleaner! Thank you

 

You can use more than one %c in a console string, and use '' to reset styles e.g.

console.log('%cvalue%c = %s', 'font-weight: bold; color: red;', '', str)

If you want some very fancy output you can use this package, which supports multi-line styled output including inline objects and HTML elements:

github.com/astoilkov/console.message

You can even use the CSS trick to display images in the console!

github.com/adriancooney/console.image

 

CSS in console.log

console.log('%cHello World!', 'font-weight: bold; font-size: 50px; color: red; text-shadow: 3px 3px 0 rgb(217,31,38), 6px 6px 0 rgb(226, 91,14), 9px 9px 0 rgb(245,221,8), 12px 12px 0 rgb(5,148,68), 15px 15px 0 rgb(2,135,206), 18px 18px 0 rgb(4,77,145), 21px 21px 0 rgb(42,21,113)');('%cHello World!', 'font-weight: bold; font-size: 50px; color: red; text-shadow: 3px 3px 0 rgb(217,31,38), 6px 6px 0 rgb(226, 91,14), 9px 9px 0 rgb(245,221,8), 12px 12px 0 rgb(5,148,68), 15px 15px 0 rgb(2,135,206), 18px 18px 0 rgb(4,77,145), 21px 21px 0 rgb(42,21,113)');

 

Console.group and console.groupEnd is also very useful.

 

Dont forget about console.time() and console.timeEnd() for performance tracking. console.trace to log track trace and many others cool features :)

 

console.trace was just the one I wanted to mention. Whenever I jut want to trace call stack to just see the flow of function calls better.

 

I had no idea, now I won’t forget! Thanks for sharing!

 

Just be aware of support though. We found that console.dir for example wasn't supported by a couple of browsers a year or so back and had to write polyfills! Only an issue if you are baking them into the app (i.e. error handling methods) rather than debugging in devtools

Oddly console.dir is supposedly widely supported even in ie9, but we found this wasn't always the case...

 

Checkout the Colored Console log extension for VSCode.

marketplace.visualstudio.com/items...

You can have Flair on all console.log with just a keypress. Also, you can change the styling directly in the extension.

 

This seems promising. I will try it. Thank you

 

Good work. Very helpful and informative.

 

All of this information is blowing my mind, especially the fact that you can style console outputs!

 

I’m glad you find it useful!

 

Had no idea this was possible, great post Leira

 

congratulations for sharing Leira

 

I like the console.table() as every now and then we deal with json response. This could come handy as it is readable.

 

Is there a css for python's log/command terminal?

 

I haven't learned Python yet so I can't help you. Hopefully another reader is able to answer this question!

 

Thanks, till now I only userd console.log, now I'm going deep with console.table ;-)

 

I love nerdy console statements

 

Great post! I didn't know about most of these. Very helpful!

 

I now prefer log points in Chrome Dev Tools. It was already possible before with conditional break points but not as easy as the log points :)

 

I have never tried log points. I will look into them. Thank you

 

Thanks for posting will help me with my exploring, debugging and testing quickly whilst learning new JavaScript concepts etc.

 

The CSS one was new to me haha, I see some nice troll error messages for my teammates coming in the future

 

Console.table is one I wasn't aware of before. I'll definitely use that one.

 

console log with css, i like that

 

Thankful
The article was very interesting

 

Long live “console.log”