chiplog

API reference

createChiplog(options)

import { createChiplog } from "chiplog";

const chiplog = createChiplog({ sink: (event) => logger[event.level](event, event.message) });
OptionTypeDefault
sink(event: FlowEvent) => voidRequired. Where finished events go
redact(key, value, path) => unknownRuns over every value entering an event
onSinkError(error, event) => voidCalled if sink throws; never rethrown
includeStackboolean | (error) => booleantrueWhether to attach error.stack
maxStagesnumber200Total stages kept
keepFirstStages / keepLastStagesnumberhalf of maxStagesRetention split
maxStringLengthnumber2048String truncation
maxKeys / maxArrayLengthnumber64Width caps
maxDepthnumber6Nesting cap
now / wallClock / randomHexfunctionsrealOverrides, mainly for tests

Instance methods

run(label, fn, seed?)

Runs fn inside a new flow and emits one event when it settles. An exception is attributed to the stage that was running and rethrown unchanged. Returns whatever fn returns.

const order = await chiplog.run("orders.create", async (flow) => {
  flow.stage("validated");
  return create();
});

runSync(label, fn, seed?)

Synchronous variant, for flows with no awaits.

wrap(label, fn)

Wraps a function so every call becomes a flow.

const charge = chiplog.wrap("billing.charge", async (amount: number) => { … });

begin(label, seed?)

Starts an ActiveFlow the caller must finish with end(). For frameworks whose lifecycle is a set of hooks rather than a wrapping middleware. Always starts a root flow — it never adopts an ambient parent. Prefer run().

seedFromHeaders(get)

Builds a FlowSeed from inbound headers: traceparent, then x-correlation-id, then x-request-id.

Ambient helpers

Operate on the flow in scope. All are silent no-ops outside one.

import { stage, set, currentFlow, correlationId, traceparent } from "chiplog";

stage("db_query", { table: "orders" });
set({ orgId });
correlationId();   // string | undefined
traceparent();     // string | undefined
currentFlow();     // Flow | undefined

The Flow handle

stage(name, meta?)Record a step
set(fields)Promote fields to the top level
fail(error, stage?)Mark failed without throwing
rename(label)Change the flow label
label()Current label
traceparent()W3C header value for this flow
correlationId, flowIdIdentifiers

ActiveFlow (returned by begin()) adds enter() and end().

FlowEvent

{
  message: string;            // "flow checkout.submit failed at charged"
  level: "info" | "error";
  flow: string;
  outcome: "ok" | "failed";
  correlationId: string;
  flowId: string;
  parentFlowId?: string;
  traceparent: string;
  startedAt: string;          // ISO
  durationMs: number;
  stageCount: number;         // including stages the cap dropped
  stages: { name: string; atMs: number; durationMs: number; meta?: object }[];
  droppedStages?: number;
  failedStage?: string;
  error?: { name: string; message: string; stack?: string; cause?: string };
  shadowedFields?: string[];
  [field: string]: unknown;   // everything passed to set()
}

Helpers

import { redactKeys, parseTraceparent, formatTraceparent } from "chiplog";

redactKeys(["email", "token"], "[redacted]");
parseTraceparent(header);            // { traceId, spanId } | null
formatTraceparent(traceId, spanId);  // "00-…-…-01"

parseTraceparent returns null for malformed or all-zero values.

Entry points

Import
chiplogCore. Zero dependencies
chiplog/honoHono middleware. hono is an optional peer
chiplog/elysiaElysia plugin. elysia is an optional peer

Runtime support

AsyncLocalStorage is required — Node 18+, Bun and Deno support it; Cloudflare Workers need a compatibility flag. It is the only runtime-specific import and is isolated in one module.