Hacker News new | past | comments | ask | show | jobs | submit login
Use console.log() like a pro (2020) (markodenic.com)
700 points by vuciv1 3 months ago | hide | past | favorite | 148 comments

Another trick that might be more practical for actually debugging is using the object shorthand. For example, instead of...

    console.log(x, y);
which contains the information you need, but lacks any useful context, try...

    console.log({x, y});
...which will print out like an object, including the key names.

The best trick for actual debugging is to leverage your browser console.

In a local build you basically have a 1:1 mapping of source code to deployed code and the debugger here basically becomes your IDE. You can hit Ctrl+Shift+P in the inspector and use the same kind of fuzzy file matcher you have in Sublime Text or VS Code, and from there you can set breakpoints, modify the code in memory, and so on. The console will reflect on the entire scope and annotate the code with their runtime values, the same as you get when working inside a JetBrains IDE.

But it's JS, so you can tweak it without committing it to disk and so you get some form of REPL driven development. I can't remember the last time I've used a console.log over setting a breakpoint in the debugger and fucking with the application state at that point to understand an issue.

You can get quite close to that after you've bundled your code and deployed it to a server, so long as you've got good source maps going on.

Print debugging is invaluable, but I can't help but think there's something of Smalltalk or Lisp in how you can mess with your app within a sandbox through the inspector, at runtime. The only thing that breaks the model is the transpilation and minification, without sourcemaps.

FYI VS Code's JS debugger speaks Chrome Debugger Protocol and allows you to use the built in Debug Console just as you would the browser's debug console, also it's possible to set browser breakpoints/etc., and you get inline value hovers.

Most IDEs, for that matter!

I bet the last time you used console.log was a race condition bug then :-)

Unfortunately that tends to print the object with collapsed values, requiring you to expand the object to actually see any of the values.

You could do console.table({x, y}) if you really want to see them initially expanded

I much prefer it being collapsed. When dealing with large objects, the firehose can be annoying.

Then, one day, after 12 hours chasing a race condition bug, you learn that when you expand objects from console.log, it shows you the current value of properties and not the values at the time of the console log. Because the values are evaluated when you click on the arrow.

Then you quit web development and start a new career.

Rite of passage for every web developer. The moment when you realize this is the moment when the universe makes sense again... after long hours of total confusion.

FWIW, it's not like it could work in any other way.

When you console.log() an object, it just stores a pointer to the object, and decorates it with an interactive label containing some text, so that it looks nice. This is fast to do, and most of the time, it's exactly what you want.

To log a static snapshot with equivalent interactive expansion capability, console.log() would have to do a general deep copy of your object - it would have to walk every pointer in the object and replace the pointee with its own recursive full deep copy, making sure to detect and handle cycles well, and keeping track of every replacement to ensure referential integrity (e.g. if two random objects A and B both have a pointer to the same object C, the copied A' and B' better both point at the same C'). An unlucky console.log() could easily copy half of your heap into the console, and god help you if you logged a DOM element.

(Also, all these copies would not be garbage-collected until you cleared the console.)

An universal deep copy is impractical to implement (notice how nobody seems to ever implement it, at all, in any programming language), and having console.log() do one would be an incredibly powerful and unpredictable footgun. Meanwhile, if you want to log a static snapshot of an object, all you need to do is to write console.log(cloneForLog(object)), where cloneForLog() is a function you wrote that does whatever copying is appropriate in your situation.

I think the only bad thing about console.log() is that this behavior is not taught to people as a core and important aspect of the function. I guess maybe if console.log() was restricted to strings, and something like console.logPresentation() was a separate function for printing objects, people would check the docs first and wouldn't be surprised.

> To log a static snapshot with equivalent interactive expansion capability, console.log() would have to do a general deep copy of your object

Not necessarily, and if it did I don't think it would address the actual problem.

console.log could do all the presentation work upfront, right then and there when you log, and provide a collapsed view of that. This would have to include detecting and cycling handles, as it would have to do for a deep copy as you mentioned. The cost would be wasting cycles on this work even if nobody looks at its result.

But where it gets dangerous is if there's any side effects in following the object tree. If visiting for logging e.g. creates nodes, or changes them, or whatever. Arguably "you get what you ask for", but this and the performance hit for generating log output before anyone really looks at it deeply, are probably the main reason things are as they are.

Technically it's V8/DOM <-> CDP <-> devtools JS -> devtools DOM -> Skia displaylist.

In the current status quo, devtools requests values through CDP as you expand the nodes.

Based on experiences with using Expand recursively on surprisingly short JSON network responses, I get the impression either the CDP I/O or the JS driving it is quite slow. Or the implementation's just accidentally quadratic.

In any case, I get the impression the right solution would be to make V8 execute and retain the deep-copy internally, then forward bits of it over CDP as requested.

Hrm, now I'm curious if the underlying mechanics that power the HeapProfiler could be readily repurposed for this.

The only fundamental issue, which is likely been the central blocker all along, is representing objects that are cyclic; the implementation would be closer to "object snapshot" than "literal deep copy".

(CDP = chrome devtools protocol, ie what gets exposed over --remote-debugging-port.)

Err, handling cycles, not cycling handles. Can't edit anymore, but funny anyway...

Yeah I don't remember how I found about this, but I remember it involved some `console.log(JSON.stringify(...))`. It's tricky to notice.

And then you can't repro the bug anymore because it was related to Mobx observables or whatever and that JSON.stringify suddenly made it work so you just... leave it there...

Fun story explaining this to the PR reviewers though :D

I was just about to mention this, I wasted a solid hour or two on this until I realised what was happening.

As I pointed out in another comment:

  console.log({x, y});
the wrapping object is being created at log time, so its values will never be changed after the fact. That could still happen with the contents of x or y themselves, but then it's no different from the original way (console.log(x, y);)

We can actually just test this. Here's some code:

    const x = {value: 0};
    x.value = 1;
Running that in the latest Chrome javascript console, we see that the first version prints `{value: 0}` and the second prints `{x: {...}}`. When you expand the second one, it will show `{x: {value: 1}}`.

The really fun part is doing this :)...

Evaluate the expression:

    const x = {value: 0};
    x.value = 1;
You get this:

    {value: 0}
    {x: {…}}
Expand the first arrow of the `x:`:

    v {x: {…}}
     > x: {value: 1}
Now evaluate:

    x.value = 3
Then expand the second arrow:

    v {x: {…}}
     > x:
         value: 3
Now if you unexpand the arrow, you get 1, but if you expand it you get 3 =)... (Well at least in chrome)

> Now if you unexpand the arrow, you get 1, but if you expand it you get 3 =)...

Moreover, if you expand arrow next to {value: 0}, you will see literally this:

    v {value: 0} [i]
        value: 3

> its values will never be changed after the fact

By this I mean, the x and y in the output will never themselves change value. They may be mutated, but they cannot be reassigned in the printed object. The printed object is exclusively referenced by the console itself, even if the nested objects within it may be referenced elsewhere.

> That could still happen with the contents of x or y themselves, but then it's no different from the original way (console.log(x, y);)

By this I mean exactly what you demonstrated, the point being that it had nothing to do with the original suggestion made by jchw.

yep, but then when I expand (click triangle) the {value: 0} line, it shows "value: 1" on the next line, only to rise the confusion.

To be fair, there is also an "i" in a square, reminding me about this behaviour.

Excuse me?! I just tried this out and I honestly never knew this until now. I can only imagine how much this has caused me trouble in the past.

You are not wrong. Is this a repl or a debugger? Because it seems to be mixture? How is that helpful?!

On Node, console.dir(x, {depth: null}) will print the object fully expanded (even in cases where console.log by itself won't). Pass something other than `null` to `depth` and it'll recurse to that depth, as well.

Doesn't help on browser, though; the options arg to console.dir isn't part of the standard there.

You can console.log(JSON.stringify(x, null, 2)) to pretty-print the object as JSON. It doesn’t look as good, but it’ll expand and show you the value as it was the moment you logged it. (As opposed to the value when you open it in dev tools)

If they're primitives most consoles will show you the values without expansion

I really like this method but unfortunately most debuggers print objects with keys in alphabetical order so there’s no way to get the keys you care about most at the top. Is there a way to rectify this?

This is going to make it significantly more janky but why not just put it into a list inside the curly brackets? That should preserve order while still triggering the object debugging feature

Posted this and then realized you beat me to it :)

Yes, I love this one!

thank you. hadn't seen that before

Or you can console.log(JSON.stringify(x, null, 2));

I think you misunderstood the intent of the GP

But this way is easier to reconcile the output, because the values logged shown are what they where at the time of the console.log(), not at the time of expansion (later).

Try this in a browser console: x={a:1,b:{c:1}};console.log(x);x.b.c=2;

then 'expand' the object, 'c' will be logged as 2, not 1

The original commenter was creating the object at log-time, though, which means it wouldn't be shared by anything else. Unless x or y is an object, but in that case the issue is completely tangential to the original suggestion

And anyway- JSON.parse(JSON.stringify(x)) would be preferable because you'd still get the browser's rich object exploration

> Unless x or y is an object, but in that case the issue is completely tangential to the original suggestion

Of course we have to assume that x and y may be objects!

I've put over 200 tips like that on my website: https://umaar.com/dev-tips/

Each tip has a textual explanation, and an animated gif if you're a visual learner (I know, I need to scrap gifs and move to regular videos).

There's a lot of tricks there which can hopefully improve your development and debugging workflows. Let me know if there are specific things you'd like to see. A few people have asked for how to find memory leaks.

Speaking of memory leaks, I once had to debug a memory leak caused by console log entries. Objects that are logged to the console are prevented from being garbage collected, even if the console was never opened. That includes DOM nodes or I think also handles to WebGL textures.

Interesting, I think that's because logged objects are "live", so you can inspect their current state, not just the state at the point of logging.

That’s interesting. I’m guessing console.log appends to the window object somewhere, which is a fairly common method of image preloading.

Great thing! Do you support rss feeds, so that I can stay up-to-date?

Amazing! Thanks so much for this.

Wow there are some cool tricks here!

In Firefox any objects you pass to console.log are expandable, so you can say console.log("my hash", h). It seems to behave the same when you say console.log("my hash %o", h).

But there is a tricky thing that has really confused me in some debugging efforts: when expanded, the object display is "live", so it always shows the current properties of the object, not the properties as they were when you printed them. But the unexpanded view shows them as they were. So for example:

    h = {foo: "bar"}
    ▶ Object { foo: "bar" }
    h.foo = "BAR"
Then you click the triangle and you see:

    ▼ {..}
    |   foo: "BAR"
    | ▶ <prototype>: Object { .. }
I don't know if that's a bug or desired behavior, but watch out for it! In the past I've used console.log(JSON.stringify(h)) to capture the full object as-is. I guess turning it back into an object would be even nicer, so you could have a deep copy to navigate.

That's an anti-feature. When you log something in traditional sense you are recording something at that time.

It's perhaps badly named, but it's also the only way it could work in practice. A general facility for expanding any logged object to arbitrary depths would be prohibitively expensive to implement with static snapshots.

I've posted about it in more detail here: https://news.ycombinator.com/item?id=26785429.

It's more that the way that the console reflects how the language works, and the console is not a log, it's a REPL (so console.output would have been a nicer name than console.log).

I think it would be more confusing if the console did not work like the rest of the language does.

> It's more that the way that the console reflects how the language works, and the console is not a log, it's a REPL (so console.output would have been a nicer name than console.log).

That is entirely irrelevant. The primary purpose of `console.log` is and has always be to generate output from normal, non-interactive programs.

And it's completely wrong, `console.log` was absolutely intended as a logging method, as evidenced by its siblings `debug`, `info`, `warn` and `error`, pretty much like every logging API out there.

It's also ahistorical revisionism "the console" was added very late into the history of the language, it and the entire console API were added by Firebug in the mid aughts. The language had been a thing for a decade at that point.

> I think it would be more confusing if the console did not work like the rest of the language does.

It would be the exact opposite. When I try to output something, my intent is to show the state of that thing at that point. That JS consoles are lazy (and even deferred) has systematically been a pain point and a pain in the ass leading to eager deep cloning to ensure I can see what I actually have on hand at that point, especially in mutation-heavy code.

I'm absolutely certain the number of times I've considered the behaviour a feature rather than an annoyance is 0.

What I expect to happen when I do `console.log(obj)` is that it called `obj.toString()` which means I'd expect it to print `[object Object]` and that if I wanted to see all the values I'd have to either serialize the object `console.log(JSON.stringify(obj))` or manually generate a string `console.log(`field1: ${obj.field1}, field2: ${obj.field2}`);

The fact that the browser provides me this convenience of a link to an expandable live object is a bonus feature. I'm glad it doesn't try to deep copy the object. If it did it would make console.log useless because of the performance overhead.

If you want to capture all the fields then `console.log({...obj})` would work. But of course any of those fields that are references to objects will be live. I wouldn't expect any thing else. A print function shouldn't be required to figure out if your deep references are circular which would be required if you wanted deep copies.

console.log can be an async function so it's not exactly a bug.

There's a more in-depth answer SO: https://stackoverflow.com/a/23392650

My whole personal site[1] is one big console.log(), right down to theme matching :D Unfortunately I'm not sure anyone has actually noticed.

1: https://itsokayitsofficial.io/

I just see a tree and a name, is that all it is? Not sure what you meant but I’m on my phone so I can’t really look at the console.

Go to the desktop version and open the dev tools console :)

No output in Safari for some reason. I see it in Chrome.

oh really? Interesting, why wouldn't Safari support the console APIs? Huh, well, I'm going to take the high road and say Safari intentionally not supported for such-and-such high-minded reasons.

I really like how that plays out as a sort of text only alternative to a site, very cool ;)

Hey thanks! I love the sense of discovery the dev console provides. When you go to someone's site and hit `F12` to see what's going on and BOOM, your confronted with a message, like they were waiting for you personally. It's those little bits of hidden magic the internet can provide.

Clever and clean, Nice work!

Much appreciated!


But you should proofread it for typos.

Thanks! My IDE is a terrible spell-checker

If you're using console.log to do debugging then it's worthwhile giving yourself a little more data to point at where a problem might lie - timings.

Open up devtools (cmd+option+j), then open the command palette (cmd+shift+P), and then search for "console", and then select "Console - Show Timestamps". Now every console output will have the high definition timestamp prepended to it. That can be really helpful if you don't want to go down the whole perf chart rabbit hole, or if you think things might be running in the wrong order due to some async weirdness.

(This probably only works in Chrome)

In Firefox, you click on the gear icon in the top right of the console, then check "Show Timestamps".

Though you probably want the per-tab Console there (ctrl-shift-k). ctrl-shift-j would give you the multiprocess browser console, which is very noisy.

All that text and no links to the reference docs to allow one to learn to fish and also learn about changes in the future: https://developer.mozilla.org/en-US/docs/Web/API/Console

Let me save someone a few minutes of confusion: generally I use console.table instead of console.dir ever since I discovered console.dir is basically unpredictable. Try using it on an Error or anything that inherits from Error and you'll see it puts out what looks like an expandable stack trace. I have no idea how or why it's implemented to do that, but basically it just varies from one object to the next and I dislike that.

Thank you for this

Since I don’t see it mentioned yet: my favorite thing to do if I’m console slumming is to use it as a comma-separated expression. You can use console.log(), foo as a single expression (eg as an arrow function return) the log is executed but its undefined return value discarded. This saves a lot of keystrokes where you’d otherwise have to wrap the function body in braces with an explicit return statement.

My (newly discovered) favorite trick is using the comma operator. Using it, you can (admittedly horribly) log anything in the middle of any expression.

This for example will call `console.log(myVar)` and still call `someFunction(myVar, someOtherArgument)`.

  myVar = "12345"
  someFunction((console.log(myVar), myVar), someOtherArgument);
Pretty handy sometimes :)

Another little trick; instead of doing:

  console.log("some label: " + JSON.stringify(someObj))
pass it as a separate parameter:

  console.log("some label: ", someObj)
and you'll get interactive expansions/manipulation in the console

No, this is totally different. In the second version, if someObj changes after it was logged, when you'll expand it you'll see the updated value. JSON.stringify freezes the value. To get the same as the first example, but interactive, you have to do:

  console.log("some label: " + JSON.parse(JSON.stringify(someObj)))

> you have to do

Technically you'd have to do

  console.log("some label: ", JSON.parse(JSON.stringify(someObj)))
> In the second version, if someObj changes after it was logged, when you'll expand it you'll see the updated value

Yes, this is something to be aware of (and is getting beaten to death throughout this comments section), but if like me you mostly use plain objects in an immutable way, you generally don't have to bother with cloning. Just keep this in the back of your head and know when it won't do what you want in a particular context.

> Just keep this in the back of your head

I would never debug with that mindstate. I don't trust myself.

It's only different if you don't treat objects as immutable.

Not gonna lie, read this hoping to pat myself on the back for being a pro, twist: learned some cool stuff, console.memory()? Neato!

Me too. `console.table` is totally new to me as well.

Another! If you inspect a console.logged function, DevTools shows you the scope it has access to.


Doesn't work in Firefox, unfortunately. :sadface:

Yeah, also it's console.memory not console.memory()

I often always do this to deep print my javascript object. console.log(JSON.stringify(myObject, null, 4));

This is great trick because the console will keep a reference to your object, not a copy, so if it changes between the time it was logged and now then the log expando shows the new value, not the old value.

By printing out the object you get a point-in-time snapshot rather than a reference to a mutable object.

Yup, learned it the hard way lol.

One missing mention is `console.groupCollapsed(label)` which is handy for nesting large dumps of data that you don't want to steal visual real estate.

Eg: console.groupCollapsed('data$ at load'); console.groupCollapsed('GridData'); console.table(data$.GridData.toList()); console.groupEnd(); console.groupCollapsed('Loads'); console.table(data$.Loads.toList()); console.groupEnd(); console.groupCollapsed('Drv'); console.table(data$.Drv.toList()); console.groupEnd(); console.groupEnd();

This will present `> data$ at load` and clicking on the chevron will open the data showing the list of entries and clicking on their chevrons will show the table for each.

> Using console.log() for JavaScript debugging is the most common practice among developers. But, there is more…

Cut your debugging time, knowledge of console.log, and mental churn in half and set up your tooling to use a `debugger` statement. The console.log method may be used heavily but it’s actually a bad practice and often leaves code littered with log statements. Even for the purpose of logging itself you should use a logging library for serious development.

You should use a debugger in every language you can for development.

Neat tricks though.

If you want to quickly log stuff without adding a log statement, you can right click the line gutter in sources and add a logpoint. Similar to a breakpoint, but logs every time it hits that line. Not modifying the source code so you don't have to remember about removing it.

Browser debuggers are amazing. I've been using them since Venkman. There's no substitute for setting a breakpoint, via the UI or a `debugger` statement, and watching your code execute a line at a time, in context. But that doesn't make console.log() a bad practice! The console is a great tool I am glad to have, especially when I remember the alternative, which was calling alert() and getting [object Object] and wanting to throw your desktop tower out the nearest window.

Use console.log for it's intended use, formatting string output, but for debugging purposes, it is a bad practice because the reason it's used usually do not usually justify the sloppy unfocused mess it leaves behind. A poorly written log statement is tech debt, as well. Debugger provides all the benefits one would expect with console.log usage:

1. You can see where you are at that moment of execution in code

2. You can see variables and arguments within scope

3. You can forget about it and a linter will pick it up or it will be ignored unless run in debugging mode

All of this allows you to focus on the actual bug and not looking for something like:

`MY SPECIAL VAR: [object Object]`

I forgot to format, let's run it again.

What about your workflow causes console.log statements to litter the code but not debugger statements? There are fewer of them?

I find use cases for both. Sometimes I want to run code and review the generated debug log, other times I want to step through interactively.

I find interactive debugging takes more concentration than a workflow of: form hypothesis, add the console.logs to prove/disprove it, run the code, and analyze the result.

Console.group is one of my favorite features but Chrome does not handle filtering it very well. Basically, if you want to filter on a certain term, all the groups will remain, even if nothing from those groups (title, subfields, otherwise) matches. There has been a Chromium bug open since 2014: https://bugs.chromium.org/p/chromium/issues/detail?id=363796

Came to post this. been following that issue for 5 years now and still no updates. Maybe if more people star it it'll eventually get some attention :\

I use the console.dir method occasionally to get the public methods and properties of HTML elements, for instance console.dir(document.body) would output all the methods and properties

Sometimes you wanna see the value of a variable but it's inside a hot function and ends up spamming/freezing your console if you log it.

Not quite a console.log statement, but Live Expression in DevTools is pretty useful for that. It's the little "eye" next to filter at the top, and it'll constantly watch an expression and show the latest value. Worst case you can assign your value to `window.myValue` and put a watch on that.

console.log can also be styled! It's a restricted subset of css so you have to hack around it, but it's good enough to make images display:

  img = $$('img')[0];
  // console.log(img);
  console.log("%cPlaceholder", `background:url("${img.src}") no-repeat cyan; border:1px solid black; padding:${img.naturalHeight}px ${img.naturalWidth}px 0 0; font-size:0; line-height:0;`);
If you paste this you should see the HN logo display in your console. Credit for the image trick goes to https://github.com/adriancooney/console.image

I use this a lot for working with animated canvases. Appending the current frame into the page is not the same since you lose the context you get from being interleaved with your other log messages.

Very important for backend is %j (e.g.: console.log('%j', variable); which can output complex json objects to the terminal. I often write it to the terminal and then copy it to an online json viewer so it is formatted in a viewable format.

Have you tried `jq`? https://stedolan.github.io/jq/

Maybe you can skip a step with pasting online.

I did not. Thanks!

Is there any way to add logging that is stripped at build time? Like in C# if I have a “Debug.WriteLine” it doesn’t exist if built in release mode. I now build ts to js with a “dev” config, could that not strip out all console.debug for me?

for what its worth i have this guy blocked on twitter - all he does is repost basic APIs you can get from MDN. classic grift playbook.

Not everybody have already learned everything. Sure, documentation is a really good source of truth, but for some people simpler introduction than a whole whitepaper's worth of information, which you get in MDN, might be more digestible.

I don't think closing on people just starting to learn is a good way to expand the field of software engineering.

This brings to light an issue I have encountered recently which has caused me to rely on console.log more than I would like.

In a recent project I have started using async/await, and seem to have lost the ability to use the debugger effectively. Breakpoints no longer seem to work properly. Its a huge negative, and im thinking of rewriting a lot of the code to remove async/await if I cant fix this issue.

Has anyone experienced this? If so, is there a way to fix it, or is this what I can expect when using async/await?

I've experienced this when the async/await is not handled natively (for example when targeting ES5/6) and chrome or the sourcemap have it difficult to map the original code to the executed js.

You can get the same behavior as console.log() without cluttering up your code by using LogPoints.



Rather than using

  %s, %i, %o, %f, etc.
You can use a templated string, like this:

  `This is a string that has been printed ${someVar} times.`

That works great for strings / numbers, not so much for objects (unless you really want to see "[object Object]" in your debug strings).

Was reading this thinking the biggest pain is being unable to wrap console.log and keep line numbers.

But some Googling [1] shows that this has now been fixed by being able to blackbox your wrapper scripts.

[1] https://gist.github.com/paulirish/c307a5a585ddbcc17242

I didn't know about .memory, .trace() and .assert(), all of which are very helpful. Up till now, I've had to add a try-throw-catch to get a stack trace and be able to follow synchronous execution flow leading up to a given function call, but console.trace will do that for me, so yay.

I used to think `console.log` was only good for soothing and assuaging the limbs of trees. Now I know better!

This hurt to read. Don't get me wrong, I salute you for it, but it still hurt...

Hehe. <3

Would be nice to also include console.table() explained on https://phpcontrols.com/blog/debugging-javascript-go-beyond-...

If I'm looking at values in node, I typically do

   console.dir(obj, { depth: null })   
However, if you need to inspect some object in a browser, don't forget you can just insert:

Which can be helpful at times.

Please don't add colours to your console.log statements. It might look nice in the Chrome console, but as soon as your messages end up elsewhere, in a terminal or text file for instance, it makes the statements look very messy.

I love adding colors to my logs whether they are bash or nodejs, but I generally check if the context is tty or not before applying colors.

Another tip I found very useful lately

You can use `$("selector")`, even without jQuery, in console. (not sure if with firefox, works with safari and chrome)

And also `$x("path")` for xpath.

But note, this works only in console, it’s not available from javascript.

Looks like Firefox gives you `document.getElementById` with `$`. You need $$("selector") for `document.querySelectorAll`. Or rather, it looks like it does something like `[...document.querySelectorAll(arg)]` (it returns an `Array` not a `NodeList`.)

You can also use $0 to get the currently selected element in the inspector.

Yep, it's an alias for document.querySelector. $$ aliases document.querySelectorAll, too.

Nice little article. Learned a new trick or two.

I'd love to see one on the step-thru debugger!

    %s – string
    %i or %d – integer
    %o or %O – object
    %f – float
Why were these ever specific types, instead of just one option that looks at the parameter type?

I could see both, but things like integer vs float couldn't be determined from the type, since you just have the single `number` type. That being said, you could just convert to an int on the value you're passing in rather than having the interpolation do it for you. Same thing with object vs string, you could pass in `obj.toString()` instead of just `obj` with an `%s`

Probably as a remnant of C's printf? Python has the same for its string formatting.

> parameter type

laughs in JS

The ones that were new and interesting to me were towards the end:

    console.time('x'); console.timeEnd('x')

Didn't know about the string interpolation. Don't see a reason to use that over built in JS string interpolation (i.e. `var is ${var]`), but still interesting.

Somethings don’t log correctly if you use the built in interpolation without using JSON.stringify. And if you use stringify it’ll actually “freeze” it and it will not be interactive.

There's pro, and then there's Steven Wittens. Open the console on his site.


I keep on coming back to debugging with print statements. I (embarrassingly) started using console.trace more often only recently.

If you select an item with the inspector (Chrome or Firefox), you can now refer to that HTML element as $0 in the console.

console.clear() is the worst. I've had to fight rogue calls to capture debugging information.

Never knew about console.memory, that will be help the next time I feel paranoia about mem leaks!

Hey, this is pretty neat! Never knew you could assert with it also. This'll come in handy!

I do `window.c = console`, and there is no looking back. It can save a lot of key strokes

I think the very fact that you have to rely on things like that when debugging speaks volumes about the language deficiencies. Or maybe it's just the tooling or environments where JS is executed?

In any case, the debugging experience is probably the biggest reason why I dislike modern web dev and tend to steer my career towards back-end.

I don’t understand your comment. Firefox and Chrome both have actual debuggers as well, but many times console.log is a faster way. What are the deficiencies you speak of?

I just think there are easier ways to debug your code than including logging statements between your lines. And in some environments I have never felt the need to do so.

I use the browser debuggers as well but I never saw a JS stack trace approaching readability of a C# stack trace, and there are also other things that make the entire debugging experience more troublesome. Maybe that's just because I have very little front-end experience (and no workflows and IDEs properly set up to work with particular frameworks). Or maybe it's because of the JS ecosystem. Threads like this one make me lean towards the latter.

If there’s an unhandled exception (or even a log message) in your JavaScript, you can click on it in the console and go straight to the source line, set a breakpoint and reload or re-trigger the code, and now you’ve got a fully interactive “stack trace” where you can inspect the values of local variables at all levels of the stack. Or you can set the debugger to automatically break on exceptions, or even to break on events or DOM modifications.

I also can’t stress enough how nice it is to have the debugger integrated into the runtime environment as opposed to an auxiliary debugger like GDB. No need to launch the debugger after my code breaks because it’s already there.

I think web technology has come a long way in the past few years, but a lot of people still seem to hold a grudge against it that I can only imagine they developed in the days of “jQuery all the things”. Are there still problems? Massively yes, but what ecosystem is perfect? I think if you don’t have much recent experience, it can’t hurt to try these things again with an open mind and see if your opinions still hold. I personally don’t have any experience with C# to be able to compare the debugging experience, but compared to Go or C/C++ I think it is massively better.

I think with a good setup including a LSP server for your editor, a good linter, and a type checker, frontend development is actually one of the more enjoyable ways to write code.

Tangential, but the only feature I really miss from browser debuggers is time travel debugging. Mozilla was working on implementing this in the Firefox DevTools as “WebReplay” [0] but it was spun out as a separate project called Replay.io [1] and I haven’t heard much about it since then.

[0]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/We...

[1]: https://www.replay.io/

You don't have to "rely" on it just like you don't have the rely on `fmt.Println()` in golang for debugging.

A language/runtime/framework gives you options and you pick your poison, browsers offer you step by step debuggers and REPL out of the box and I actually think it's awesome in terms of debugging, you can of course only use console.log(), and it's a good place to start. When you're ready to move on there are many more useful tabs in the browser devtools to debug with.

What worries me is that, apparently, there are many people with a lot of web dev experience who haven't moved on. Why haven't they moved on? I don't understand it.

I have yet to see a mid-level engineer using the same method when debugging C# code.

Despite all that, I try to refrain from strong statements about technologies. Just throwing around ideas.

console.table O_O

Wow, since when it was in JS?

Amazing, console.dir definitely beats console.log(JSON.stringify(...))

99% of Frontend developers don't know about the debugger/developer tools. You can even log stuff without ever writing/compiling directly from Chrome Developer Tools

>> 99% of Frontend developers don't know about the debugger/developer tools.

How is that possible?

I think what he meant is:

> Most frontend developers in my experience don't know about developer tools.

I still don't know how that's possible though.

OK. I would argue that if you spend that much time making your console look pretty (beyond useful), then you're either not spending enough time on things that matter (everything else, that the user may see) or you're grossly over budgeted.

Btw none of this is as useful as a breakpoint. Type `debugger;` in your code, refresh chromium or what have you with the dev panel open and inspect everything, jump over etc. ad nauseam. Pro tips use IntelliJ or webstorm for a really nice experience debugging.

Applications are open for YC Winter 2022

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact