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

@chhax1618/probe

v1.0.0

Published

Lightweight, zero-dependency Express middleware for server latency profiling — p50/p95/p99, slow-request capture, event-loop lag, GC tracking.

Readme

probe

Lightweight, zero-dependency Express middleware for server latency profiling.

What it gives you

| Feature | Description | |---|---| | Per-route percentiles | p50 / p95 / p99 / max / avg, sorted by p95 desc | | Slow-request capture | Full context dump: concurrent reqs, event-loop lag, memory, correlation ID | | Queue-time detection | Measures nginx/LB → app delay from X-Request-Start header | | Server diagnostics | CPU load, memory pressure, heap %, uptime — with auto-verdict | | Event-loop lag | monitorEventLoopDelay histogram, reset per slow event | | GC pause tracking | PerformanceObserver on gc entries, correlated to slow requests | | Downstream isolation | Wrap DB/API calls to see if lag is yours or a dependency | | Correlation ID | Auto-generates/propagates x-request-id | | < 0.1ms overhead | Typed-array ring buffer, no allocs in hot path | | Zero dependencies | Single file, ~260 lines |

Install

# npm (once published)
npm i @chhax1618/probe

# or just copy index.js into your project
cp index.js /your/project/lib/probe.js

Quick Start

const express = require('express');
const createProbe = require('@chhax1618/probe');  // or './lib/probe'

const probe = createProbe({ slowThresholdMs: 300 });
const app = express();

// Mount FIRST — before body parsers, auth, etc.
app.use(probe.middleware);

// Report endpoint (add auth in production!)
app.get('/latency-report', probe.reportRoute);

app.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
app.listen(3000);

Nginx Setup (for queue-time detection)

Add this to your nginx location block:

proxy_set_header X-Request-Start "t=${msec}";

The probe will automatically detect this header and calculate how long the request sat in nginx/LB before reaching your app. No code changes needed.

API

createProbe(options?)

| Option | Default | Description | |---|---|---| | slowThresholdMs | 500 | Requests above this are flagged slow | | ringSize | 500 | Max samples per route (ring buffer) | | reportAuth | null | fn(req) → bool gate for report endpoint | | enableGC | true | Track GC pause durations | | enableCorrelationId | true | Auto x-request-id | | enableQueueTime | true | Detect nginx/LB queue time | | enableServerInfo | true | Include server diagnostics in full report | | instanceId | $HOSTNAME | Tag for multi-instance deploys | | onSlow | console.warn | Custom handler fn(slowCtx) |

Instance methods

| Method | Returns | |---|---| | probe.middleware | Express middleware — mount first | | probe.report() | Route stats array (sorted by p95 desc) | | probe.summary() | Quick health-check object | | probe.serverInfo() | Full server diagnostics + verdict | | probe.queueReport() | Nginx/LB queue time percentiles | | probe.reportRoute | Express handler — mount on /latency-report | | probe.wrapCall(label, fn) | Returns wrapped async fn with latency tracking | | probe.downstreamReport() | Stats for wrapped downstream calls | | probe.gcReport() | Recent GC pause entries | | probe.reset() | Clear all stats |

Expected Output

GET /latency-report — standard report

{
  "summary": {
    "uptimeSec": 3600.12,
    "totalRequests": 48210,
    "inFlight": 3,
    "routeCount": 12,
    "globalP95Ms": 245.8,
    "slowTotal": 47,
    "queueTime": { "count": 48210, "avgMs": 1.2, "p50Ms": 0.8, "p95Ms": 3.1, "p99Ms": 8.4, "maxMs": 42 },
    "eventLoopLagMs": 0.45,
    "memoryRssMb": 128.3
  },
  "routes": [
    { "route": "GET /orders/:id", "count": 8420, "avgMs": 45.2, "p50Ms": 32.1, "p95Ms": 245.8, "p99Ms": 412.3, "maxMs": 890.1, "slowCount": 47 },
    { "route": "POST /users",     "count": 3200, "avgMs": 12.4, "p50Ms": 8.9,  "p95Ms": 38.2,  "p99Ms": 62.1,  "maxMs": 120.5, "slowCount": 0 }
  ],
  "queueTime": { "count": 48210, "avgMs": 1.2, "p50Ms": 0.8, "p95Ms": 3.1, "p99Ms": 8.4, "maxMs": 42 },
  "downstream": [
    { "label": "db:findOrder", "count": 8420, "avgMs": 40.1, "p50Ms": 28.0, "p95Ms": 210.3, "p99Ms": 380.1, "maxMs": 850.2, "slowCount": 35 },
    { "label": "cache:get",    "count": 12000, "avgMs": 0.8,  "p50Ms": 0.5,  "p95Ms": 1.9,   "p99Ms": 3.2,   "maxMs": 12.1,  "slowCount": 0 }
  ]
}

GET /latency-report?full — includes server diagnostics + GC

{
  "summary": { "..." : "same as above" },
  "routes": [ "..." ],
  "downstream": [ "..." ],
  "gc": [
    { "ts": 1720267200000, "ms": 4.21, "kind": 1 },
    { "ts": 1720267260000, "ms": 12.8, "kind": 2 }
  ],
  "server": {
    "platform": "linux",
    "arch": "x64",
    "nodeVersion": "v20.19.1",
    "pid": 12345,
    "uptime": { "processSec": 3600.12, "systemSec": 864000.5 },
    "cpu": {
      "cores": 4,
      "model": "Intel Xeon E5-2686 v4",
      "loadAvg1m": 2.8,
      "loadAvg5m": 2.1,
      "loadAvg15m": 1.6,
      "loadPerCore1m": 0.7,
      "usagePct": 68.4
    },
    "memory": {
      "system": { "totalMb": 8192, "freeMb": 1024, "usedPct": 87.5 },
      "process": { "rssMb": 128.3, "heapUsedMb": 82.1, "heapTotalMb": 96.0, "externalMb": 4.2, "heapPct": 85.5 }
    },
    "verdict": ["CPU_HIGH", "MEMORY_HIGH", "HEAP_PRESSURE"]
  }
}

Slow-request log (automatic on console.warn)

{
  "route": "GET /orders/:id",
  "method": "GET",
  "status": 200,
  "durationMs": 812.34,
  "queueTimeMs": 3.2,
  "totalTimeMs": 815.54,
  "timestamp": "2026-07-06T12:00:00.000Z",
  "concurrentAtStart": 14,
  "memoryRssMb": 128.3,
  "eventLoopLagMs": 4.21,
  "eventLoopLagMaxMs": 18.90,
  "requestId": "abc-123"
}

How to Read the Output

Where is the latency coming from?

| Symptom | Look at | Likely cause | |---|---|---| | High queueTimeMs, low durationMs | Queue time report | Nginx/LB overloaded — not your app | | High durationMs, high downstream p95 | Downstream report | DB or external API is slow | | High durationMs, low downstream p95 | Event loop lag | Your code is blocking (CPU, sync ops) | | High eventLoopLagMs on slow requests | Slow-request logs | Sync blocking code — JSON.parse, regex, etc. | | Slow requests cluster after GC pauses | GC report timestamps | Memory pressure / leak | | verdict: CPU_SATURATED | Server info | Server overloaded — scale up/out | | verdict: MEMORY_PRESSURE | Server info | Swapping — reduce memory or scale | | verdict: HEAP_PRESSURE | Server info | GC thrashing — memory leak likely |

Verdict flags

| Flag | Meaning | |---|---| | HEALTHY | All good | | CPU_HIGH | Load per core > 0.7 | | CPU_SATURATED | Load per core > 1.0 — requests will queue | | MEMORY_HIGH | System memory > 75% used | | MEMORY_PRESSURE | System memory > 90% — swap likely | | HEAP_PRESSURE | V8 heap > 85% — GC overhead increasing |

Downstream Call Wrapping

// Wrap any async function to track its latency separately
const findOrder = probe.wrapCall('db:findOrder', async (id) => {
  return db.orders.findById(id);
});

const callPaymentAPI = probe.wrapCall('http:payments', async (data) => {
  return fetch('https://api.payments.com/charge', { method: 'POST', body: JSON.stringify(data) });
});

// Use them normally — latency is tracked automatically
app.get('/orders/:id', async (req, res) => {
  const order = await findOrder(req.params.id);
  res.json(order);
});

Caveats

  • In-memory only — resets on restart. Call probe.report() on an interval and persist the JSON for history.
  • Per-instance — in clustered/multi-pod setups, each instance has its own stats. Use instanceId and aggregate externally.
  • Queue time requires header — configure nginx with proxy_set_header X-Request-Start "t=${msec}";