In‑Depth Guide to Node.js Inspector: Usage, Principles, and Implementation

This article provides a comprehensive tutorial on using Node.js Inspector for local and remote debugging, automatic detection, data collection (heap snapshots and CPU profiles), dynamic activation, and a detailed walkthrough of its internal architecture and communication flow between the client, WebSocket, and V8 Inspector.

ByteDance Web Infra
ByteDance Web Infra
ByteDance Web Infra
In‑Depth Guide to Node.js Inspector: Usage, Principles, and Implementation

1. Using Node.js Inspector

Node.js ships with a powerful Inspector that can debug code, collect heap snapshots, CPU profiles, and other runtime data. It can be started locally with node --inspect or remotely by exposing a WebSocket endpoint.

1.1 Local Debugging

Example of a simple HTTP server:

const http = require('http');
http.createServer((req, res) => {
  res.end('ok');
}).listen(80);

Run it with node --inspect httpServer.js and open Chrome DevTools to see the Node.js debugging button.

1.2 Remote Debugging

Start the server on a remote host: node --inspect=0.0.0.0:8888 httpServer.js Connect from the browser using a URL like:

devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=1.1.1.1:8888/abcd1234

1.3 Automatic Detection

Chrome can auto‑detect Node.js processes via chrome://inspect/#devices, then click configure and add the remote address.

1.4 Data Collection

Use the V8 Inspector to collect HeapSnapshot and CPU Profile data. The following code opens a session and writes a CPU profile to a file:

const inspector = require('inspector');
const http = require('http');
const fs = require('fs');
function getCpuprofile(req, res) {
  const session = new inspector.Session();
  session.connect();
  session.post('Profiler.enable', () => {
    session.post('Profiler.start', () => {
      setTimeout(() => {
        session.post('Profiler.stop', (err, {profile}) => {
          if (!err) fs.writeFileSync('./profile.cpuprofile', JSON.stringify(profile));
          session.disconnect();
          res.end('ok');
        });
      }, 3000);
    });
  });
}
http.createServer((req, res) => {
  if (req.url == '/debug/getCpuprofile') getCpuprofile(req, res);
  else res.end('ok');
}).listen(80);

1.5 Dynamic Enabling

Because the default inspector is unsafe, you can enable it on demand:

const inspector = require('inspector');
const http = require('http');
let isOpened = false;
function getHTML() {
  return `<html><meta charset="utf-8"/><body>Copy this URL to a new Tab to start debugging devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=${inspector.url().replace("ws://", "")}</body></html>`;
}
http.createServer((req, res) => {
  if (req.url == '/debug/open') {
    if (!isOpened) { isOpened = true; inspector.open(); }
    res.end(getHTML());
  } else if (req.url == '/debug/close') {
    if (isOpened) { inspector.close(); isOpened = false; }
    res.end('ok');
  } else {
    res.end('ok');
  }
}).listen(80);

2. Inspector Debugging Principle

When a browser connects, it establishes a WebSocket connection and exchanges JSON‑RPC messages defined by the Inspector protocol. Commands such as Debugger.scriptParsed and Debugger.setBreakpointByUrl are used to set breakpoints and retrieve script sources.

3. Implementation Details of Node.js Inspector

3.1 Initialization

Node creates an inspector::Agent and starts an I/O thread:

inspector_agent_ = std::make_unique<inspector::Agent>(this);
inspector_agent_->Start(...);
inspector_agent_->StartIoThread();

3.2 Connection Handling

A Libuv TCP server listens on a port, accepts connections, and upgrades HTTP to WebSocket. The flow is:

Accept TCP socket → ServerSocket::Accept Create SocketSession and InspectorSocket Parse HTTP request, send 101 Switching Protocols Switch handler to

WsHandler

3.3 Protocol Upgrade

After the upgrade, the server sends a 101 response and replaces the protocol handler:

inspector->SwitchProtocol(new WsHandler(inspector, std::move(tcp)));

3.4 Data Flow from Client to V8

WebSocket frames are decoded, then forwarded through SocketSession::Delegate::OnWsFrameInspectorSocketServer::MessageReceivedInspectorIoDelegate::MessageReceivedCrossThreadInspectorSession::Dispatch → V8 ChannelImpl which finally calls session_->dispatchProtocolMessage.

3.5 Data Flow from V8 to Client

V8 sends a response via ChannelImpl::sendResponse, which goes through IoSessionDelegate, enqueues a TransportAction::kSendMessage, and is written back to the WebSocket by WsHandler::WriteTcpHolder::WriteRaw → Libuv uv_write.

4. Summary

Node.js Inspector provides a powerful way to debug, profile, and collect runtime data from Node.js processes, supporting both local and remote scenarios, dynamic activation, and a well‑structured internal architecture that bridges the client, WebSocket, and V8 Inspector.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

debuggingNode.jsWebSocketV8ProfilingInspector
ByteDance Web Infra
Written by

ByteDance Web Infra

ByteDance Web Infra team, focused on delivering excellent technical solutions, building an open tech ecosystem, and advancing front-end technology within the company and the industry | The best way to predict the future is to create it

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.