npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-loop-detective

v2.0.0

Published

Detect event loop blocking & lag in running Node.js apps without code changes or restarts

Readme

node-loop-detective 🔍

Detect event loop blocking, lag, and slow async I/O in running Node.js apps — without code changes or restarts.

$ loop-detective 12345

✔ Connected to Node.js process
  Profiling for 10s with 50ms lag threshold...

⚠ Event loop lag: 312ms at 2025-01-15T10:23:45.123Z
⚠ Event loop lag: 156ms at 2025-01-15T10:23:48.456Z
🌐 Slow HTTP: 2340ms GET api.example.com/users → 200
🔌 Slow TCP: 1520ms db-server:3306

────────────────────────────────────────────────────────────
  Event Loop Detective Report
────────────────────────────────────────────────────────────
  Duration:  10023ms
  Samples:   4521
  Hot funcs: 12

  Diagnosis
────────────────────────────────────────────────────────────
   HIGH  cpu-hog
         Function "heavyComputation" consumed 62.3% of CPU time (6245ms)
         at /app/server.js:42
         → Consider breaking this into smaller async chunks or moving to a worker thread

   1. heavyComputation
      ██████████████░░░░░░ 6245ms (62.3%)
      /app/server.js:42:1

  ⚠ Slow Async I/O Summary
    Total slow ops: 3

    🌐 HTTP — 2 slow ops, avg 1800ms, max 2340ms
      GET api.example.com/users
        2 calls, total 3600ms, avg 1800ms, max 2340ms

    🔌 TCP — 1 slow ops, avg 1520ms, max 1520ms
      db-server:3306
        1 calls, total 1520ms, max 1520ms

How It Works

  1. Sends SIGUSR1 to activate the Node.js built-in inspector (or connects to --port)
  2. Connects via Chrome DevTools Protocol (CDP) with automatic retry and exponential backoff
  3. Injects a lightweight event loop lag monitor
  4. Tracks slow async I/O (HTTP, DNS, TCP, fetch) via monkey-patching
  5. Captures a CPU profile to identify blocking code
  6. Analyzes the profile for common blocking patterns
  7. Disconnects cleanly — minimal impact on your running app

If the target process exits during profiling, loop-detective detects it immediately, reports any lag/I/O events collected so far, and exits with code 2.

Install

npm install -g node-loop-detective

Usage

# Basic: profile a running Node.js process by PID
loop-detective <pid>

# Connect to an already-open inspector port
loop-detective --port 9229

# Profile for 30 seconds with 100ms lag threshold
loop-detective -p 12345 -d 30 -t 100

# Detect slow I/O with a 1-second threshold
loop-detective -p 12345 --io-threshold 1000

# Connect to a remote inspector (Docker, K8s, remote server)
loop-detective --host 192.168.1.100 --port 9229

# Save raw CPU profile for Chrome DevTools / speedscope
loop-detective -p 12345 --save-profile ./profile.cpuprofile

# Continuous monitoring mode
loop-detective -p 12345 --watch

# JSON output (for piping to other tools)
loop-detective -p 12345 --json

# List available targets (main thread + worker threads)
loop-detective --port 9229 --list-targets

# Profile a specific worker thread
loop-detective --port 9229 --target 1

# Generate HTML report
loop-detective -p 12345 --html report.html

Options

| Flag | Description | Default | |------|-------------|---------| | -p, --pid <pid> | Target Node.js process ID | — | | -H, --host <host> | Inspector host (remote connections) | 127.0.0.1 | | -P, --port <port> | Inspector port (skip SIGUSR1) | — | | -d, --duration <sec> | Profiling duration in seconds | 10 | | -t, --threshold <ms> | Event loop lag threshold | 50 | | -i, --interval <ms> | Lag sampling interval | 100 | | --io-threshold <ms> | Slow I/O threshold | 500 | | --save-profile <path> | Save raw CPU profile to file | — | | --no-io | Disable async I/O tracking | false | | --list-targets | List available inspector targets and exit | — | | --target <index> | Connect to a specific target (worker thread) | 0 | | --html <path> | Generate self-contained HTML report | — | | -j, --json | Output as JSON | false | | -w, --watch | Continuous monitoring | false |

What It Detects

CPU / Event Loop Blocking

| Pattern | Description | |---------|-------------| | cpu-hog | Single function consuming >50% CPU | | json-heavy | Excessive JSON parse/stringify | | regex-heavy | RegExp backtracking | | gc-pressure | High garbage collection time | | sync-io | Synchronous file I/O calls | | crypto-heavy | CPU-intensive crypto operations |

Slow Async I/O

| Type | What It Tracks | |------|---------------| | 🌐 HTTP/HTTPS | Outgoing HTTP requests — method, target, status code, duration | | 🌐 Fetch | Global fetch() calls (Node.js 18+) — method, target, status, duration | | 🔍 DNS | DNS lookups — callback and promise API (dns.lookup + dns.promises.lookup) | | 🔌 TCP | TCP connections — target host:port, connect time (covers databases, Redis, etc.) |

Each slow I/O event includes the caller stack trace, so you know exactly which code initiated the slow operation.

Programmatic API

const { Detective } = require('node-loop-detective');

const detective = new Detective({
  pid: 12345,
  inspectorHost: '127.0.0.1',  // or remote host
  duration: 10000,
  threshold: 50,
  interval: 100,
  ioThreshold: 500,
});

detective.on('lag', (data) => console.log('Lag:', data.lag, 'ms'));
detective.on('slowIO', (data) => console.log('Slow I/O:', data.type, data.target, data.duration, 'ms'));
detective.on('profile', (analysis) => {
  console.log('Heavy functions:', analysis.heavyFunctions);
  console.log('Patterns:', analysis.blockingPatterns);
});

await detective.start();

Requirements

  • Node.js >= 16
  • Target process must be running Node.js
  • On Linux/macOS: permission to send signals to the target process
  • On Windows: target must be started with --inspect flag (SIGUSR1 not available)

How is this different from clinic.js / 0x?

Those are great tools, but they require you to start your app through them. loop-detective attaches to an already running process — perfect for production debugging. It also tracks slow async I/O (HTTP, DNS, TCP) which those tools don't focus on.

License

MIT