prom-client adapter
Este conteúdo não está disponível em sua língua ainda.
If your app already uses prom-client (the de-facto Node /
Prometheus library) for its non-actor metrics,
promClientRegistry(...) lets the framework’s metrics live in
the same prom-client registry — one /metrics endpoint, all
metrics together.
import client from 'prom-client';import { ActorSystem,} from 'actor-ts';import { MetricsExtensionId, promClientRegistry, PromClientAdapterOptions,} from 'actor-ts/metrics';
// Your app's existing prom-client registry. `client.register` is// the default global one; here we use a fresh Registry for clarity.const registry = new client.Registry();// ... register your existing prom-client metrics on `registry` ...
const system = ActorSystem.create('my-app');
// Point the metrics extension at a registry that writes straight// into prom-client. Every framework counter / gauge / histogram// from here on lands in `registry` alongside your app metrics.const promAdapterOptions = PromClientAdapterOptions.create() .withClient(client) .withRegistry(registry) .withNamePrefix('actor_ts_');system.extension(MetricsExtensionId).useRegistry( promClientRegistry(promAdapterOptions),);
// Your existing /metrics endpoint now emits framework + app metrics:get(async () => ({ status: 200, body: await registry.metrics(), contentType: registry.contentType,}));promClientRegistry(...) returns a MetricsRegistry that writes
straight through to prom-client — every framework counter,
gauge, and histogram mutation lands in your prom-client registry
synchronously. There’s no background sync, no interval, and no
second copy of the values: prom-client holds the canonical state,
and your existing register.metrics() route emits everything.
When to use it
Section titled “When to use it”Two main reasons:
- Existing prom-client usage — your code has been emitting metrics via prom-client; you don’t want to maintain two registries.
- One scrape endpoint — your operators expect a single
/metricsURL combining all your metrics.
If you don’t already use prom-client, prefer the framework’s native Prometheus exporter — no extra dependency.
One registry, one reader
Section titled “One registry, one reader”The bridge keeps no copy of what it writes. prom-client holds
the values, so collect() on the returned registry is empty —
permanently, by design — and the registry says so by declaring
collectable: false.
That is fine for the path this adapter exists for: you read the
metrics through register.metrics(), exactly as you read your own.
It matters for anything in the framework that reads the other
way, through collect(). There are two, and both refuse rather
than report zero:
- The management
GET /metricsroute answers503while this bridge is installed, instead of200with an empty body. An empty body is a valid Prometheus scrape — the target staysup=1and the framework’s series simply stop existing, so alerts written over them never fire again. - The DevTools overview shows
unavailablefor the three figures it reads from the registry — processed messages, mailbox drops and handler latency. Its Throughput chart leaves themessages / sline out for the same reason and names it in the legend, rather than drawing a flat line at zero: a chart is more persuasive than a tile, and that line would be making exactly the claim the tile beside it refuses to make. The dead-letter line next to it is counted off the event stream and stays, as does everything else on the page — it comes from the actor tree and the event stream and stays correct.
So: don’t enable enableMetricsEndpoint alongside this bridge —
scrape your own prom-client route instead, which has all the same
series. And read framework metrics in DevTools only on a system
using the framework’s own registry.
Nothing about the writes is affected. Every framework counter,
gauge and histogram is on your prom-client route, live, including
actor_mailbox_dropped_total — the drop counter the DevTools tile
cannot show you.
Configuration
Section titled “Configuration”type PromClientAdapterOptionsType = { client: PromClientLike; // the prom-client namespace: import client from 'prom-client' registry: PromClientRegistryLike; // the Registry to publish into — typically client.register namePrefix?: string; // prefix applied to every metric name — default: '' maxSeriesPerFamily?: number; // per-family cardinality cap — default: 10 000, 0 disables};client and registry are mandatory — the bridge has nothing to
publish into without them. client is the prom-client namespace
you already import; registry is the Registry it writes into
(usually client.register, the default global registry). The
plain object above works anywhere the builder does — the builder is
the documented default.
namePrefix lets you namespace framework metrics so the
bridge-sourced families are easy to spot in the exposition:
const prefixedOptions = PromClientAdapterOptions.create() .withClient(client) .withRegistry(registry) .withNamePrefix('actorts_');promClientRegistry(prefixedOptions);
// → actorts_messages_delivered_total, actorts_members_up, ...Cardinality cap
Section titled “Cardinality cap”The bridge enforces the same per-family cap as the in-process
registry (default 10 000 label tuples, 0 disables), and it needs
to more urgently: prom-client mints a series inside its own
Counter / Gauge / Histogram on every .labels(...) call and
never expires one, so an unbounded label grows your process’s
resident memory, not just the scrape body.
const cappedOptions = PromClientAdapterOptions.create() .withClient(client) .withRegistry(registry) .withMaxSeriesPerFamily(50_000);promClientRegistry(cappedOptions);Past the cap the bridge rewrites the tuple to the family’s own
label names with every value set to __overflow__, so prom-client
sees at most maxSeriesPerFamily + 1 distinct tuples per family.
Reusing the declared names is not cosmetic: prom-client fixes a
metric’s label names at construction and throws on a .labels(...)
carrying anything else, so a synthetic marker name would fail the
very call meant to contain the damage.
See cardinality discipline
for bucketize, the way to not reach the cap in the first place.
Removing a series
Section titled “Removing a series”remove
forwards to prom-client’s own Metric.remove(labels), so a tuple whose
subject is gone leaves both sides at once — the bridge’s cap tally and
the exposition prom-client renders. Dropping only one of the two would
be worse than not removing at all: a freed slot with the series still
being scraped, or the reverse.
That is the one place the adapter needs more of prom-client than the
construct-and-mutate surface, so the client you pass in must expose
remove on its Counter / Gauge / Histogram — every release since
v11.2 does. A hand-rolled stand-in that omits it is a compile error
rather than a silent divergence.
Peer dependency
Section titled “Peer dependency”npm install prom-client# or: bun add prom-clientprom-client is a peer — only required if you use this
adapter.
Where to next
Section titled “Where to next”- Observability overview — the bigger picture.
- Core metrics — the framework’s metric primitives that flow through the bridge.
- Prometheus exporter — the framework-native alternative when you don’t need prom-client.
