pyroscope-nodejs

Continuous CPU and heap profiling for Node.js apps, exported directly to Grafana Pyroscope.

SDK
npm
v0.6.3
51stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
72/100Good
Development Activity84
Maintenance88
Community48
Maturity56
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture78
Code Quality80
Innovation75
Learning Curve55

Pyroscope Node.js is Grafana’s official profiling client for Node.js services. It wraps V8’s sample-based profiler (via @datadog/pprof) in a continuous, timer-driven collection loop that periodically snapshots wall-clock and heap profiles and ships them as pprof-encoded data to a Pyroscope server, so teams get an always-on flamegraph history instead of a one-off snapshot taken during an incident.

Beyond the push-based continuous mode, the package ships Express and Fastify middleware that expose standard /debug/pprof/* endpoints for pull-based scraping, dynamic per-request tag labels for slicing flamegraphs by things like tenant or route, and a source-mapper that resolves compiled JavaScript stack frames back to original TypeScript source lines. It is purpose-built as the Node.js SDK for the Grafana Pyroscope profiling backend rather than a general-purpose profiling toolkit.

What You Get

  • A single init() call that reads server address, auth, and sampling config from options or environment variables and starts a continuous profiling loop
  • Wall-clock and heap profilers built on @datadog/pprof’s native V8 bindings, coordinated through a shared ContinuousProfiler scheduler
  • Express and Fastify middleware exposing /debug/pprof/heap and /debug/pprof/profile for pull-mode scraping with existing pprof tooling
  • Dynamic label support (wrapWithLabels, setLabels) to tag profile samples with request- or tenant-specific metadata for later filtering
  • A built-in source mapper that translates compiled JS stack frames back to original TypeScript file/line numbers in the resulting flamegraphs

Common Use Cases

  • Running always-on CPU/heap profiling in production Node.js services to catch regressions before they page anyone
  • Correlating performance hotspots with business dimensions (tenant, route, job type) via per-request profile labels
  • Exposing pprof-compatible debug endpoints on an Express or Fastify service for ad-hoc profiling with standard pprof tooling
  • Feeding continuous profiling data into Grafana alongside existing metrics and traces for full observability correlation

Under The Hood

Architecture The package is organized into three layers: profilers/ wraps @datadog/pprof’s native V8 profiler behind a small Profiler<TStartArgs> interface, with ContinuousProfiler (src/profilers/continuous-profiler.ts) adding a timer-driven schedule/export loop shared by both the wall and heap profilers inside PyroscopeProfiler. middleware/ (express.ts, fastify.ts) offers a pull-mode path that drives the same Profiler interface directly on an HTTP request rather than through the continuous scheduler, trading some code reuse for a simpler synchronous request/response flow. pyroscope-api-exporter.ts implements a ProfileExporter that gzips and POSTs pprof-encoded buffers to a /ingest endpoint with bearer or basic auth and tenant headers, while sourcemapper.ts post-processes profiles to resolve bundled JS frames back to original source locations. The top-level index.ts composes these pieces behind a small init/start/stop public API, so most of the coordination complexity is hidden from consumers.

Tech Stack Written in TypeScript and built to dual ESM/CJS output via separate tsconfig.cjs.json/tsconfig.esm.json configs and custom build scripts in tools/. Core dependency is @datadog/pprof for native V8 profiling bindings, alongside pprof-format for protobuf-based pprof encoding, debug for namespaced logging, and source-map for stack resolution. Express and Fastify are optional peer dependencies gated behind peerDependenciesMeta.optional, so the middleware is opt-in and doesn’t force either framework on consumers. The project uses Yarn 4 (Berry) with pinned resolutions for several transitive dependencies, Husky for pre-commit hooks, and ships releases through a GitHub Actions workflow using release-please and npm’s OIDC trusted publishing rather than a static npm token.

Code Quality Tests use Node’s built-in node:test runner rather than a separate framework, and several suites (express.test.ts, fastify.test.ts, profiler.test.ts) spin up a real Express server to assert actual HTTP ingest behavior end-to-end instead of mocking the network layer. Types are used throughout, including generic Profiler<TStartArgs> and ContinuousProfilerInput<TStartArgs> interfaces that keep the wall and heap profiler implementations structurally consistent. ESLint and Prettier are configured with lint-staged via Husky, and a dedicated GitHub Actions workflow runs the test suite on every change. The network export path wraps upload failures in try/catch and logs rather than throwing, favoring resilience of the host application over guaranteed delivery of any single profile.

API Design The public surface is intentionally small: init(config) plus start/stop cover the common case, with configuration resolvable from environment variables to minimize required boilerplate for container-based deployments. Dynamic per-request labeling (wrapWithLabels, getLabels/setLabels) is a differentiated capability that lets consumers correlate flamegraph data with application-level context rather than just wall-clock time. Deprecated aliases (startCpuProfiling, getWallLabels) are kept alongside their replacements for backward compatibility, and the pull-mode middleware reuses standard /debug/pprof/* paths so existing pprof-based tooling works without modification.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search