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:
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.
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| Instrument | Type | Attributes |
|---|---|---|
resilix.executions | counter | key, verdict |
resilix.execution.duration | histogram (ms) | key, verdict |
resilix.rejections | counter | key, reason, policy |
resilix.state.transitions | counter | key, from, to, reason |
resilix.breaker.{state,failureRate,slowRate,windowSize} | gauge | key |
resilix.bulkhead.{inFlight,limit,utilisation} | gauge | key |
@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.