Debugging and inspecting code

One area that new Node.js developers struggle with is debugging code. Instead of having the inspector, we have a system where the first crash will dump some information to our screen and instantly kick us to the command-line. This can be seen with the following code:

const thing = 'this';
console.log(thing);
thing = 10;

Here, we can see that we are trying to reassign a constant, so Node.js is going to throw an error similar to the following one:

TypeError: Assignment to constant variable. 
at Object.<anonymous> (C:CodeCh5ad_code.js:3:7)
at Module._compile (internal/modules/cjs/loader.js:774:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:785:10)
at Module.load (internal/modules/cjs/loader.js:641:32)
at Function.Module._load (internal/modules/cjs/loader.js:556:12)
at Function.Module.runMain (internal/modules/cjs/loader.js:837:10)
at internal/main/run_main_module.js:17:11

While this can be scary, it also shows us where the error is. The first line in this stack trace tells us it is at line 3, character 7.

A stack trace is a way for a system to provide the developer with information about which functions were calling what. In our case, Object.<anonymous> was called by Module.__compile, and so on. This can help when a good chunk of the stack is ours and the error actually occurs farther up.

With this information, we know how to correct the issue, but what do we do if we want to break on a specific statement or a specific line? This is where the inspector system comes into play. Here, we can utilize statements that are similar to the ones we saw in the web version of our code. If we insert a debug statement in the middle of our code, our command line will stop at that point.

Let's create some rudimentary code to showcase this. The following code should give us plenty to showcase the use of the inspector:

const fun = function() {
const item = 10;
for(let i = 0; i < item; i++) {
const tempObj = {};
tempObj[i] = "what " + i;
}
return function() {
console.log('we will have access to other things');
const alternative = 'what';
debugger;
return item;
}
}

console.log('this is some code');
const x = 'what';
debugger;
fun()();

This code will allow us to play around with various parts of the inspector. If we run the npm inspect bad_code.js command, we should break on the call to fun. We are greeted with a Terminal interface that states we are in debug mode. Now that we have stopped execution here, we can set up a watcher. This allows us to capture various variables and expressions and see what their results are on the next break. Here, we set up a watcher on the x variable by executing watch('x') in the debugger. From here, if we type next, we will move to the next line. If we do this a couple of times, we will notice that once we pass the assignment of our variable, the watcher will change the x variable from undefined to 10.

This can be especially helpful when we need to debug a stateful system that is sharing state among quite a few objects. It can also be helpful when we are trying to see what we have access to. Let's set up a few more watchers so that we can see what their values are when our next debug statement is hit. Set up watchers on the following variables: item, tempObj, and alternative. Now, type cont. This will move us to our next debugger statement. Let's see what's printed out by our watchers. When we move to the next point, we will see that tempObj and x are not defined, but that we have access to item and alternative.

This is what we expect, seeing how we are scoped inside the outer fun function. There's much more we can do with this version of the inspector, but we can also hook up to the inspector that we are used to.

Now, if we use the following command to run our code, we will be able to attach the debug tools to our script:

 > node --inspect bad_code.js 

With this, we will get an address that we can connect to. Let's do just that. We will also need to have some long-running code; otherwise, the script will exit and we will have nothing to listen to. Let's move back to the named_pipe.js example. Run node --inspect -–experimental-modules named_pipe.js.

We should get something that looks like the following:

Debugger listening on ws://127.0.0.1:9229/6abd394d-d5e0-4bba-8b28-69069d2cb800

If we head to the following address in our Chrome browser, we should be greeted with a familiar sight:

chrome-devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=<url>

Now, we have the full power of the inspector from Chrome for our Node.js code. Here, we can see that if we connect to our named pipe server with our named_pipe_child.js file, we will be greeted with the console logs in the debugger. Now, if we add debugger statements, we should get breakpoints inside of the inspector. If we add a debug statement right when a socket connects to us, when we connect with our child socket, we will be able to run through our code the same way we can in the browser! This is a great way to debug and step through our code.

We are also able to memory profile. If we head to the Memory tab and create a heap snapshot, we will get a nice dump of our memory. It should look quite familiar to what we have seen already.

With all of this under our belt, we can move onto more complex topics surrounding Node.js.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.22.136