Debugging — the process of finding and fixing defects in software — can be a challenging task to do in all languages. Node.js is no exception.
Luckily, the tooling for finding these issues improved a lot in the past period. Let’s take a look at what options you have to find and fix bugs in your Node.js applications!
We will dive into two different aspects of debugging Node.js applications — the first one will be logging, so you can keep an eye on production systems, and have events from there. After logging, we will take a look at how you can debug your applications in development environments.
Logging takes place in the execution of your application to provide an audit trail that can be used to understand the activity of the system and to diagnose problems to find and fix bugs.
For logging purposes, you have lots of options when building Node.js applications. Some npm modules are shipped with built in logging that can be turned on when needed using the debug module. For your own applications, you have to pick a logger too! We will take a look at pino.
Before jumping into logging libraries, let’s take a look what requirements they have to fulfil:
debug
module of Node.jsRecommendation: use for modules published on npm
`debug` is a tiny Node.js debugging utility modeled after the Node.js core’s debugging technique.
Let’s see how it makes your life easier! Imagine that you have a Node.js module that sends serves requests, as well as send out some.
// index.js
const debugHttpIncoming = require('debug')('http:incoming')const debugHttpOutgoing = require('debug')('http:outgoing')
let outgoingRequest = { url: 'https://risingstack.com' }
// sending some requestdebugHttpOutgoing('sending request to %s',outgoingRequest.url)
let incomingRequest = { body: '{"status": "ok"}' }
// serving some request debugHttpOutgoing('got JSON body %s', incomingRequest.body)
Once you have it, start your application this way:
DEBUG=http:incoming,http:outgoing node index.js
The output will be something like this:
Also, the debug module supports wildcards with the *
character. To get the same result we got previously, we simply could start our application with DEBUG=http:* node index.js
.
What’s really nice about the debug module is that a lot of modules (like Express or Koa) on npm are shipped with it — as of the time of writing this article more than 14.000 modules.
pino
logger moduleRecommendation: use for your applications when performance is key
Pino is an extremely fast Node.js logger, inspired by bunyan. In many cases, pino is over 6x faster than alternatives like bunyan or winston:
benchWinston*10000: 2226.117ms benchBunyan*10000: 1355.229ms benchDebug*10000: 445.291ms benchLogLevel*10000: 322.181ms benchBole*10000: 291.727ms benchPino*10000: 269.109ms benchPinoExtreme*10000: 102.239ms
Getting started with pino is straightforward:
const pino = require('pino')()
pino.info('hello pino') pino.info('the answer is %d', 42)pino.error(new Error('an error'))
The above snippet produces the following log lines:
{"pid":28325,"hostname":"Gergelys-MacBook-Pro.local","level":30,"time":1492858757722,"msg":"hellopino","v":1}
{"pid":28325,"hostname":"Gergelys-MacBook-Pro.local","level":30,"time":1492858757724,"msg":"the answer is 42","v":1}
{"pid":28325,"hostname":"Gergelys-MacBook-Pro.local","level":50,"time":1492858757725,"msg":"an error","type":"Error","stack":"Error: an error\n at Object.<anonymous> (/Users/gergelyke/Development/risingstack/node-js-at-scale-debugging/pino.js:5:12)\n at Module._compile (module.js:570:32)\n at Object.Module._extensions..js (module.js:579:10)\n at Module.load (module.js:487:32)\n at tryModuleLoad (module.js:446:12)\n at Function.Module._load (module.js:438:3)\n at Module.runMain (module.js:604:10)\n at run (bootstrap_node.js:394:7)\n at startup (bootstrap_node.js:149:9)\n at bootstrap_node.js:509:3","v":1}
Node.js ships with an out-of-process debugging utility, accessible via a TCP-based protocol and built-in debugging client. You can start it using the following command:
$ node debug index.js
This debugging agent is a not a fully featured debugging agent — you won’t have a fancy user interface, however, simple inspections are possible.
You can add breakpoints to your code by adding the debugger
statement into your codebase:
const express = require('express') const app = express()
app.get('/', (req, res) => { debugger res.send('ok') })
This way the execution of your script will be paused at that line, then you can start using the commands exposed by the debugging agent:
The V8 inspector integration allows attaching Chrome DevTools to Node.js instances for debugging by using the Chrome Debugging Protocol.
V8 Inspector can be enabled by passing the --inspect
flag when starting a Node.js application:
$ node --inspect index.js
In most cases, it makes sense to stop the execution of the application at the very first line of your codebase and continue the execution from that. This way you won’t miss any command execution.
$ node --inspect-brk index.js
I recommend watching this video in full-screen mode to get every detail!
Most modern IDEs have some support for debugging applications — so does VS Code. It has built-in debugging support for Node.js.
What you can see below, is the debugging interface of VS Code — with the context variables, watched expressions, call stack and breakpoints.
Image credit: Visual Studio Code
One of the most valuable features of the integrated Visual Studio Code debugger is the ability to add conditional breakpoints. With conditional breakpoints, the breakpoint will be hit whenever the expression evaluates to true.
If you need more advanced settings for VS Code, it comes with a configuration file, .vscode/launch.json
which describes how the debugger should be launched. The default launch.json
looks something like this:
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Launch Program", "program": "${workspaceRoot}/index.js" }, { "type": "node", "request": "attach", "name": "Attach to Port", "address": "localhost", "port": 5858 } ] }
For advanced configuration settings of launch.json
go to https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes.
For more information on debugging with Visual Studio Code, visit the official site: https://code.visualstudio.com/docs/editor/debugging.
If you have any questions about debugging, please let me know in the comments section.
In the next episode of the Node.js at Scale series, we are going to talk about Node.js Post-Mortem Diagnostics & Debugging.
Originally published at blog.risingstack.com on April 25, 2017.