Diagnostics & Diagnostic Report
Interactive debugging assumes you can attach to a process and reproduce the problem. Production incidents rarely cooperate: a process crashes at 3am, hangs intermittently, or leaks memory over days. For these, Node provides diagnostics — tooling that captures the runtime's state for after-the-fact analysis. The headline feature is the Diagnostic Report: a single JSON file summarizing the process's stacks, resource usage, and environment at a moment of failure. This page covers diagnostic reports, the broader diagnostics toolbox (async_hooks, trace events, perf_hooks, heap/CPU profiles), and how to wire them up for production.
The Diagnostic Report
# Auto-generate a report whenever the process crashes (uncaught exception): node --report-on-fatalerror --report-uncaught-exception app.js # Also trigger on a fatal signal (e.g. SIGUSR2) for a hung process: node --report-on-signal --report-signal=SIGUSR2 app.js # Control where reports are written: node --report-directory=/var/log/node-reports app.js
Trigger a report programmatically
// Write a report on demand — e.g. inside an error handler or a health probe:
process.report.writeReport() // returns the filename
process.report.writeReport('crash-detail.json')
// Or capture it as an object without writing a file:
const report = process.report.getReport()
logger.error({ report: JSON.parse(report) }, 'captured diagnostic report')What's inside a report
Section | Tells you |
|---|---|
| Why/when it was generated, Node version, command line, PID |
| The JS call stack at the moment of capture |
| The C++ stack — useful for native-addon or runtime crashes |
| Heap size, used/available memory, GC stats |
| Active handles & requests — leaked sockets, timers, pending I/O |
| CPU time, max RSS, page faults |
| The process environment (beware: may contain secrets) |
The wider diagnostics toolbox
Tool | Purpose | Invoke |
|---|---|---|
Diagnostic report | Process snapshot on crash/hang |
|
CPU profile | Where CPU time goes |
|
Heap snapshot | What's retained in memory |
|
Trace events | Timeline of V8/Node internal events |
|
| Measure your own timings & event-loop lag |
|
| Track async resource lifecycle |
|
Auto heap snapshot when memory is about to blow — invaluable for OOM crashes
// Writes a heap snapshot just before the process hits the heap limit: // node --heapsnapshot-near-heap-limit=2 app.js // Or write one on demand: import v8 from 'node:v8' const file = v8.writeHeapSnapshot() // load the .heapsnapshot in DevTools Memory tab
Measuring with perf_hooks
import { performance, monitorEventLoopDelay } from 'node:perf_hooks'
// Measure event-loop lag — the key Node health signal:
const h = monitorEventLoopDelay({ resolution: 20 })
h.enable()
setInterval(() => {
console.log('event-loop p99 lag (ms):', (h.percentile(99) / 1e6).toFixed(1))
}, 5000)
// Time a specific operation precisely:
performance.mark('start')
await doExpensiveWork()
performance.mark('end')
performance.measure('expensive-work', 'start', 'end')
console.log(performance.getEntriesByName('expensive-work')[0].duration, 'ms')A production diagnostics checklist
Enable reports on fatal errors —
--report-on-fatalerror --report-uncaught-exceptionso every crash leaves a snapshot.Enable a signal trigger —
--report-on-signalto capture a hung process on demand without killing it.Set
--heapsnapshot-near-heap-limitso OOM crashes leave an analyzable heap.Write artifacts to a persistent, access-controlled volume — not ephemeral container storage, and not world-readable (they hold secrets).
Monitor event-loop lag with
perf_hooksand alert on the p99.Keep symbol/source maps for the deployed build so captured stacks are readable.