Skip to content

Telemetry

Guarding undici (resilix/undici)

resilix/fetch covers the WHATWG API, but Node services reach the network through undici's Dispatcher — which is what fetch, undici.request and most SDK HTTP layers sit on. Guarding the dispatcher covers every call site at once, including ones you do not own:

ts
import { Agent, setGlobalDispatcher } from "undici";
import { resilixInterceptor } from "resilix/undici";

setGlobalDispatcher(
  new Agent().compose(
    resilixInterceptor({ policies: [breaker({ slowCallMs: 3_000 }), limiter()] }),
  ),
);

Latency is time to first byte, taken at onResponseStart — never the time to drain the body. A refused request never reaches the network; the RejectedError arrives through onResponseError so it surfaces like any other undici failure.

Requires undici >= 7 — undici 7 renamed the whole handler surface, and compose() exists on 6.x too, so an undici 6 install would get a silently inert interceptor. undici is an optional peer; core stays at zero dependencies.

timeoutMs, retry and hedge are not available through this adapter. dispatch is callback-driven and returns synchronously, so the executor cannot wrap it — compose undici's own interceptors.retry and headersTimeout alongside instead. Every policy works unchanged.

Telemetry (resilix/otel)

Built in, not a plugin. Under 1% of opossum users instrument their breakers, which means almost nobody has data at the moment they need it.

ts
import { metrics } from "@opentelemetry/api";
import { otel } from "resilix/otel";

const instrument = otel({ meter: metrics.getMeter("checkout") });
const api = pipeline({ policies: [...], observers: [instrument] });
instrument.observeGauges(api);   // pull-based gauges
InstrumentTypeAttributes
resilix.executionscounterkey, verdict
resilix.execution.durationhistogram (ms)key, verdict
resilix.rejectionscounterkey, reason, policy
resilix.state.transitionscounterkey, from, to, reason
resilix.breaker.{state,failureRate,slowRate,windowSize}gaugekey
resilix.bulkhead.{inFlight,limit,utilisation}gaugekey

@opentelemetry/api is an optional peer dependency — core stays at zero deps. Without a meter, otel() is a no-op, so tests need no OTel install. Observers are dispatched through a swallowing wrapper: a failing exporter can neither influence nor break an admission decision.

MIT licensed. Zero runtime dependencies.