Skip to content

TelemetryDaemon

A lightweight background daemon that collects per-step spans and exports them in batches. Completely external to core — wired via the FlowHooks API with zero coupling.

Import

typescript
import { TelemetryDaemon, consoleExporter } from "flowneer/plugins/telemetry";

Usage

typescript
import { TelemetryDaemon, consoleExporter } from "flowneer/plugins/telemetry";
import { FlowBuilder } from "flowneer";

const telemetry = new TelemetryDaemon({
  exporter: consoleExporter,
  flushIntervalMs: 5000,
  maxBuffer: 100,
});

const flow = new FlowBuilder<State>();
// Attach telemetry hooks — each call generates isolated traceId + spans
flow._setHooks(telemetry.hooks());

flow.startWith(stepA).then(stepB).then(stepC);

await flow.run(shared);

// Flush remaining spans on shutdown
process.on("SIGTERM", () => telemetry.stop());

Constructor Options

OptionTypeDefaultDescription
exporterTelemetryExporterconsoleExporterWhere to send spans
flushIntervalMsnumber5000How often to flush the span buffer (ms)
maxBuffernumber100Force flush after this many buffered spans

Span Object

typescript
interface Span {
  traceId: string; // per-run unique ID
  spanId: string; // per-step unique ID
  parentId?: string; // spans inside sub-flows point to their parent
  name: string; // e.g. "fn[0]", "batch[2]"
  startMs: number; // Unix ms
  endMs: number;
  durationMs: number;
  status: "ok" | "error";
  attrs: Record<string, unknown>;
}

Custom Exporter

Implement TelemetryExporter to send spans to any backend (OpenTelemetry, Jaeger, Datadog, etc.):

typescript
import type { TelemetryExporter, Span } from "flowneer/plugins/telemetry";

const otlpExporter: TelemetryExporter = {
  export(spans: Span[]) {
    return fetch("http://otel-collector:4318/v1/traces", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ spans }),
    });
  },
};

const telemetry = new TelemetryDaemon({ exporter: otlpExporter });

consoleExporter

A built-in exporter that pretty-prints spans to console.log:

[telemetry] fn[0] ok 142ms { stepType: "fn", stepIndex: 0 }
[telemetry] fn[1] ok 8ms { stepType: "fn", stepIndex: 1 }

TelemetryDaemon Methods

MethodDescription
hooks<S, P>()Returns FlowHooks to attach to a flow. Call once per flow instance.
flush()Force-flush the current buffer immediately
stop()Clear the flush timer and flush remaining spans
record(span)Manually record a span (advanced)

Multiple Flows

hooks() creates an isolated traceId and span context each time it's called, so multiple concurrent flows don't bleed into each other:

typescript
const flow1 = new FlowBuilder<StateA>();
flow1._setHooks(telemetry.hooks()); // traceId: "abc..."

const flow2 = new FlowBuilder<StateB>();
flow2._setHooks(telemetry.hooks()); // traceId: "xyz..."