Runtime recorder setup

Turn on automatic instrumentation, scope each request or job, then optionally tune limits and redaction.

Enable automatic instrumentation

Rasputin wraps your functions so it can show what ran when an error happened. Recorded state is kept only while a request or job is running, and is attached when that work throws.

Compiled JavaScript

Use this in production. Build as you already do, then run rasputin-instrument on the output. Start the process normally — no extra flags.

{
  "scripts": {
    "build": "tsc && rasputin-instrument dist",
    "start:node": "node dist/app.js",
    "start:bun": "bun dist/app.js"
  }
}

Have TypeScript write .js.map files next to the output so recorded functions still point at your .ts sources.

Running TypeScript directly

rasputin-instrument rewrites compiled .js files on disk. It does not run when you start TypeScript with Bun. Use --preload / --import so files are wrapped as they load.

{
  "scripts": {
    "start:bun": "bun --preload @rasputin-ai/node/instrument/bun src/app.ts",
    "start:node": "node --import @rasputin-ai/node/instrument/node dist/app.js"
  }
}

Use those flags while you run TypeScript locally. In production, instrument at build time instead — wrapping files at startup is slower and uses more memory.

Scope each unit of work

Instrumentation records function calls. An execution tells Rasputin which request, job, or task those calls belong to. Wrap each unit of work:

const scope = rasputin.execution.createScope({
  kind: 'job',
  name: 'sync-invoices',
});

try {
  return await scope.run(() => syncInvoices());
} catch (error) {
  const observation = scope.observeError(error);
  rasputin.captureException(error, { observation });
} finally {
  scope.finish();
}

scope.run() executes the callback under this recording's async context and returns exactly what the callback returned. It does not wrap, replace, or observe Promises. You own await, observeError, captureException, and finish().

If automatic instrumentation is not available — for example because the app is bundled into a single file — mark the functions you care about:

const syncInvoices = rasputin.execution.trace(
  'src/jobs/sync-invoices.ts:syncInvoices',
  async () => {
    // ...
  },
);

Configuration

Recording is on by default once instrumentation and executions are in place. Change these only to exclude noisy code, redact sensitive fields, or keep less state.

FieldDefaultWhat it does
enabledtrueTurns runtime-state capture on or off.
maxEventsPerExecution500Maximum events kept for one execution.
maxCapturedCallsPerFunction3Detailed successful calls kept before similar calls are summarized.
maxActiveMemoryBytes64 MiBMaximum recorder memory shared across active executions.
maxDepth3Maximum captured value depth.
maxObjectKeys30Maximum properties kept from one object.
maxArrayElements20Maximum items kept from one array, map, or set.
maxStringLength500Maximum characters kept from one string.
maxSerializedValueBytes16 KiBMaximum retained size of one captured value.
runtimeStateTargetBytes256 KiBSoft size goal for the snapshot sent with an error; extra reconstruction data is dropped first.
maxRuntimeStateBytes512 KiBHard size limit for that snapshot; values are dropped before call history.
redactKeys[]Extra case-insensitive property names to redact.
excludeSources[]Source globs or function-name patterns to exclude.
const rasputin = RasputinInit({
  // ...required options
  executionRecorder: {
    excludeSources: ['src/logger/**', 'packages/shared-logger/**'],
    redactKeys: ['customerEmail'],
    maxEventsPerExecution: 200,
  },
});

Sensitive data: runtime state can include arguments and return values. Add sensitive property names to redactKeys.

Diagnostics

These counters are local to the current process and reset when it restarts.

const { recorder, transport } = rasputin.getStats();