DEV Community

Leira Sánchez
Leira Sánchez

Posted on

Like `console.log` But Better

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);
Enter fullscreen mode Exit fullscreen mode

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' }
Enter fullscreen mode Exit fullscreen mode

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

console.table(array);
Enter fullscreen mode Exit fullscreen mode

console.table(array)

console.table(object);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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); //
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

clicked

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

Top comments (91)

Collapse
 
tylerlwsmith profile image
Tyler Smith

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"}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
leirasanchez profile image
Leira Sánchez

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

Collapse
 
vbelolapotkov profile image
Vasily Belolapotkov

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

Collapse
 
ben profile image
Ben Halpern

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!

Collapse
 
cathyc93 profile image
Cathy Casey-Richards

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

Collapse
 
daveblythe profile image
Dave Blythe (he/him)

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

Collapse
 
dionnyprensa profile image
Dionny Prensa

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

Collapse
 
bernardbaker profile image
Bernard Baker

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.

Collapse
 
spiralx profile image
James Skinner

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

Thread Thread
 
bernardbaker profile image
Bernard Baker

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?

Thread Thread
 
spiralx profile image
James Skinner

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

Thread Thread
 
bernardbaker profile image
Bernard Baker

Must have been the wrong example.

Thread Thread
 
bernardbaker profile image
Bernard Baker

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();
Thread Thread
 
spiralx profile image
James Skinner

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.

Thread Thread
 
bernardbaker profile image
Bernard Baker

Thanks for explaining that to me.

Thread Thread
 
spiralx profile image
James Skinner

No problem!

Collapse
 
davidmaxwaterman profile image
Max Waterman

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

What's the story there? Any more detail?

Thread Thread
 
bernardbaker profile image
Bernard Baker

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.

Thread Thread
 
davidmaxwaterman profile image
Max Waterman • Edited

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."

Thread Thread
 
bernardbaker profile image
Bernard Baker

The developer docs from MDN explain it well.

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

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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)?

Thread Thread
 
bernardbaker profile image
Bernard Baker

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.

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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.

Thread Thread
 
bernardbaker profile image
Bernard Baker • Edited
var myVar = {a: true};
var myVar2 = {};
myVar2.b = myVar; // {b: {a: true})
myVar.a = false;
console.log(myVar2); // === {b: {a:false}}
Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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

Thread Thread
 
bernardbaker profile image
Bernard Baker

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.

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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

Thread Thread
 
bernardbaker profile image
Bernard Baker

Have you used generators?

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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?

Thread Thread
 
bernardbaker profile image
Bernard Baker

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.

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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.

Thread Thread
 
bernardbaker profile image
Bernard Baker

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.

Thread Thread
 
davidmaxwaterman profile image
Max Waterman • Edited

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?

Thread Thread
 
bernardbaker profile image
Bernard Baker

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...
}
Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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().

Thread Thread
 
bernardbaker profile image
Bernard Baker • Edited

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.

Thread Thread
 
bernardbaker profile image
Bernard Baker • Edited

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();
Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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/

Thread Thread
 
bernardbaker profile image
Bernard Baker

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?

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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.

Thread Thread
 
bernardbaker profile image
Bernard Baker

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?

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

What do you define as a platform?

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

Thread Thread
 
bernardbaker profile image
Bernard Baker

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

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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'.

Thread Thread
 
bernardbaker profile image
Bernard Baker

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

Thread Thread
 
davidmaxwaterman profile image
Max Waterman

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.

Collapse
 
leirasanchez profile image
Leira Sánchez

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

Collapse
 
jamesthomson profile image
James Thomson

Don't forget about console.dir! 🙂

Collapse
 
navin_moorthy profile image
Navin

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

Collapse
 
easilybaffled profile image
Danny Michaelis

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))
Collapse
 
bashunaimiroy profile image
Bashu Naimi-Roy

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

Collapse
 
leirasanchez profile image
Leira Sánchez

I will definitely check it out. Thank you!

Collapse
 
devtronic profile image
Julian Finkler
let firstName = "John";
let lastName = "Doe";
console.log('First Name: %s, Last Name: %s', firstName, lastName);
// First Name: John, Last Name: Doe
Collapse
 
metruzanca profile image
Samuele Zanca

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.

Collapse
 
leirasanchez profile image
Leira Sánchez

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

Collapse
 
jerp86 profile image
José Eduardo Rodrigues Pinto

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)');

Collapse
 
spiralx profile image
James Skinner • Edited

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

Collapse
 
kosovych profile image
Yaroslav Kosovych

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

Collapse
 
markohologram profile image
Marko A

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.