Let me first describe the following behavior, and then let me know if this ever sounds like you. You have code that looks like this:
const result = await getSomeData().catch(handleErr)
console.log('result', result)
But, you have a large application, so you repeat that snippet at least a hundred times. Other parts of your code depend on result
, and when those other parts don't look right, you panic and frantically start adding more console.log
statements because "the code was supposed to be done yesterday":
console.log('result.id', result.id)
console.log('typeof result.id', typeof result.id)
...
Hopefully you don't practice the above behavior because then in all those places you have the debug statements, you'll have to remember to clean them out. Luckily, there are far easier ways to debug your code without requiring all the effort you think you might need.
I'm going to try my best to keep this post brief and organize this post from easy to easiest, because I know when you're frantically trying to debug something, you want the answer asap.
1) Spying on variables.
First, if you must absolutely spy on a given variable, please quit doing this:
console.log('someVariable', someVariable)
Yes, the above is easy to do, and I'm guilty of that bad habit, but the following is even easier:
console.log({ someVariable })
And yet, there is something even easier than the above. If you have Chrome dev tools, you can create something called "logPoints" (as @nickytonline mentioned; see also logpoints). They are exactly like breakpoints, but instead of breaking, messages get logged at those lines: (using the image he referenced)
Specifically, here is something simple I did earlier today to help a new dev.to friend out. I just fired up the Chrome dev tools snippet editor, created the snippet, set the logPoint (which gets organized under "breakpoints") and ran the code:
Resulted in:
So, I was able to eliminate the need to type "console.log" (11 characters); I became more efficient in my debugging efforts. Plus, I can easily disable (but keep) the logPoint simply by unchecking the checkbox. Since the logPoints are all organized in one place, this makes it easy to disable all, or enable all logging if the logPoint pins are in a myriad of places! But wait, we can do even better!
2) The debug
module
TL;DR - YES, you can use it in the browser!
TL;DR2 - YES, you can import
it!
Check your "node_modules" folder for the "debug" folder! Considering the module is downloaded 56M+ times per week, there is a good chance you already have it somewhere on your machine! Even if you haven't listed it as a dependency, since so many projects use it, it is very likely that at least one of your dependencies shipped with it. For example, if/when you installed socket.io-client
, you also installed the debug module without realizing it!
While I have high confidence that you may have used it to debug your server-side code like this:
server.js
const debug = require('debug')('server')
terminal:
DEBUG="server" node server.js
What you may not know is that the debug module can also be used in the browser!
Not only can you use it in the browser, you can import the debug module using the import
syntax you've been familiar with:
myComponent.js
:
import Debug from 'debug'
const debug = Debug('myComponent')
...
const result = await getSomeData().catch(handleError)
debug({ result })
...
Then to actually see the debug statements in the browser's console log, you don't set any environment variables but instead just set the debug variable in localStorage
. But...whatever you do, avoid setting that variable in your own code! It is far safer to set it in your Chrome dev tools! This way, your code doesn't accidentally ship with debug logging enabled.
So now, when you have that variable set, all the debug statements will log to the console for myComponent.js
. If you want to debug multiple files, each file can get it's own or shared debug string, in which case the localStorage.debug
variable just needs to be a comma-separated string or wildcard ('*'):
localStorage.debug = 'myComponent, otherComponent' // logs for both components
localStorage.debug = '*' // all debug log statements in browser will log to console
Many modules that depend on the debug module already have debug strings that they used to help themselves (and you) debug what is going on with their module. Two such modules are socket.io-client
and nuxt-socket-io
. This means that when you want to debug the IO going into and out of each part of your component, you don't need to write a single console.log
statement! You simply set the localStorage.debug
variable to the correct string(s):
localStorage.debug = 'socket.io-client:socket' // Monitor socket.io-client
localStorage.debug ='nuxt-socket-io, socket.io-client:socket' // debug even more...
// Then, when it is desired to mute the logs...simply make debug undefined:
localStorage.debugX ='nuxt-socket-io, socket.io-client:socket' // debug nothing just by putting an X there (or whatever you like!)
So, when I have localStorage.debug
set to this:
I get logs that look like this:
socket.io-client:socket emitting packet with ack id 1 +11s
socket.io-client:socket emitting event ["chatMessage","Hi, this is a chat message from IO server!"] +507ms
nuxt-socket-io Vuex listener received data +11s {evt: "chatMessage", resp: "Hi, this is a chat message from IO server!"}
socket.io-client:socket emitting event ["chatMessage","Hi, this is another chat message from IO server!"] +504ms
nuxt-socket-io Vuex listener received data +507ms {evt: "chatMessage", resp: "Hi, this is another chat message from IO server!"}
socket.io-client:socket calling ack 1 with ["It worked! Received msg: {\"id\":\"abc123\"}"] +41ms
However, when I wrote the debug statements, I only had to write:
debug('some message', { data })
But...the log entries consist of other useful pieces of information that I didn't have to think about coding, such as: the file and line producing that log entry, and the time between debug statements. If the logs were to become lengthy, Chrome dev tools makes it simple to save the console logs with a right-click and menu selection.
3) The "debugger" keyword.
Oftentimes, the code that runs in the browser is a gigantic uglified version of your source code. Trying to insert a needle of a breakpoint into a very messy haystack can be time-consuming. Fortunately, the "debugger" keyword is built into the language and can be used to break on a point of interest:
function myCodeAintWorkin(arrrrgggh) {
let thisThing;
debugger; // <-- runtime will break at this point! (even if this line is buried in the uglified code at line 1112442)
// Some buggy code here
}
So, going back to the original CarsArray
snippet, I could have also debugged the filtered
variable like this:
So, while, I did not have to type "console.log" (11 chars), I did have to type "debugger" (8 chars), but the additional benefit I got was that just by looking a few inches to the right, I was able to see the local scope instantly and fiddle with the properties as I desired. All this without having to write several more console.log
statements:
In this above image, I was able to try out a second "filtered2" statement by toggling the "age" property of the Dodge to less than 10 years. In case I doubted my first filtered statement, I could try changing the properties and assert that the new list of cars older than 10 years consists of one less car.
As cool as the "debugger" keyword is, it may be a very good idea to remove the line when your code is ready to be merged in to your team's codebase, because your team will most likely not want their code to break on your debug statements.
Conclusion
Whether or not the term "tool" applies to all three debugging approaches mentioned above I think can be up to debate. The first method takes advantage of JS object structuring, which you use as a tool to spy on variables (console.log({ someVar }
). The second approach uses one of the most popular debugging utilities in the JS ecosystem, combined with Chrome Dev Tools to help you debug in the browser. Lastly, the third method is the "debugger" keyword, which along with Chrome Dev Tools, you use like a tool to figure out application state at a given line in code.
I tried my best to order the tools by ease-of-use. How "easy" a certain debugging approach may be is certainly subjective. I personally find the second approach to be the easiest, but many more may find the third to be easiest. Either way, even if you are an experienced developer, I hope you found this post helpful.
Top comments (2)
does
turn a variable into an object anywhere in JS of just in console.log ?
Just in console.log :)