News for hackers. Stuff that matters.
Re:Use console.log() like a pro (2020) by jchw on Monday April 12, 2021 @11:16AM (#26780311)
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.
Re:Use console.log() like a pro (2020) by ljm on Monday April 12, 2021 @05:08PM (#26785217)
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.

Re:Use console.log() like a pro (2020) by meetups323 on Monday April 12, 2021 @06:45PM (#26786120)
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.
Re:Use console.log() like a pro (2020) by ConnorLeet on Monday April 12, 2021 @07:31PM (#26786441)
Most IDEs, for that matter!
Re:Use console.log() like a pro (2020) by quickthrower2 on Monday April 12, 2021 @10:36PM (#26787571)
I bet the last time you used console.log was a race condition bug then :-)
Re:Use console.log() like a pro (2020) by tshaddox on Monday April 12, 2021 @11:41AM (#26780648)
Unfortunately that tends to print the object with collapsed values, requiring you to expand the object to actually see any of the values.
Re:Use console.log() like a pro (2020) by dekerta on Monday April 12, 2021 @01:00PM (#26781898)
You could do console.table({x, y}) if you really want to see them initially expanded
Re:Use console.log() like a pro (2020) by ollien on Monday April 12, 2021 @11:56AM (#26780892)
I much prefer it being collapsed. When dealing with large objects, the firehose can be annoying.
Re:Use console.log() like a pro (2020) by pjerem on Monday April 12, 2021 @12:04PM (#26781006)
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.

Re:Use console.log() like a pro (2020) by newsbinator on Monday April 12, 2021 @12:26PM (#26781353)
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.
Re:Use console.log() like a pro (2020) by TeMPOraL on Monday April 12, 2021 @05:28PM (#26785429)
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.

Re:Use console.log() like a pro (2020) by anyfoo on Monday April 12, 2021 @07:54PM (#26786592)
> 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.

Re:Use console.log() like a pro (2020) by exikyut on Tuesday April 13, 2021 @05:47AM (#26790131)
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.)

Re:Use console.log() like a pro (2020) by anyfoo on Tuesday April 13, 2021 @02:00AM (#26788571)
Err, handling cycles, not cycling handles. Can't edit anymore, but funny anyway...
Re:Use console.log() like a pro (2020) by drinchev on Monday April 12, 2021 @02:46PM (#26783406)
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.
Re:Use console.log() like a pro (2020) by inbx0 on Monday April 12, 2021 @03:37PM (#26784100)
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...
Re:Use console.log() like a pro (2020) by drinchev on Monday April 12, 2021 @03:57PM (#26784361)
Fun story explaining this to the PR reviewers though :D
Re:Use console.log() like a pro (2020) by matja on Monday April 12, 2021 @12:07PM (#26781047)
I was just about to mention this, I wasted a solid hour or two on this until I realised what was happening.
Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @12:17PM (#26781215)
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);)
Re:Use console.log() like a pro (2020) by evan_ on Monday April 12, 2021 @12:28PM (#26781390)
We can actually just test this. Here's some code:

    const x = {value: 0};
    console.log(x);
    console.log({x});
    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}}`.
Re:Use console.log() like a pro (2020) by Folcon on Monday April 12, 2021 @12:55PM (#26781812)
The really fun part is doing this :)...

Evaluate the expression:

    const x = {value: 0};
    console.log(x);
    console.log({x});
    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)
Re:Use console.log() like a pro (2020) by Lex-2008 on Monday April 12, 2021 @01:14PM (#26782098)
> 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
Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @01:22PM (#26782242)
> 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.

Re:Use console.log() like a pro (2020) by Lex-2008 on Monday April 12, 2021 @12:49PM (#26781716)
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.

Re:Use console.log() like a pro (2020) by spondyl on Monday April 12, 2021 @10:09PM (#26787433)
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.
Re:Use console.log() like a pro (2020) by randomsearch on Monday April 12, 2021 @04:11PM (#26784537)
You are not wrong. Is this a repl or a debugger? Because it seems to be mixture? How is that helpful?!
Re:Use console.log() like a pro (2020) by JonathonW on Monday April 12, 2021 @04:07PM (#26784495)
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.

Re:Use console.log() like a pro (2020) by josephg on Monday April 12, 2021 @05:03PM (#26785166)
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)
Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @12:00PM (#26780944)
If they're primitives most consoles will show you the values without expansion
Re:Use console.log() like a pro (2020) by bobbylarrybobby on Monday April 12, 2021 @12:55PM (#26781813)
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?
Re:Use console.log() like a pro (2020) by aardvarkr on Monday April 12, 2021 @01:30PM (#26782355)
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
Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @11:59AM (#26780933)
Posted this and then realized you beat me to it :)
Re:Use console.log() like a pro (2020) by tomduncalf on Monday April 12, 2021 @12:06PM (#26781025)
Yes, I love this one!
Re:Use console.log() like a pro (2020) by dylan604 on Monday April 12, 2021 @11:41AM (#26780651)
thank you. hadn't seen that before
Re:Use console.log() like a pro (2020) by thinkingkong on Monday April 12, 2021 @11:50AM (#26780791)
Or you can console.log(JSON.stringify(x, null, 2));
Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @12:02PM (#26780974)
I think you misunderstood the intent of the GP
Re:Use console.log() like a pro (2020) by matja on Monday April 12, 2021 @12:10PM (#26781083)
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

Re:Use console.log() like a pro (2020) by brundolf on Monday April 12, 2021 @12:16PM (#26781186)
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

Re:Use console.log() like a pro (2020) by da39a3ee on Monday April 12, 2021 @12:44PM (#26781650)
> 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!