Overview
Prometheus counters, gauges, cumulative histogram families, collectors, and an authenticated Elysia exposition plugin for AbsoluteJS services.
@absolutejs/metricsv0.4.1betaObservabilityTurn every metrics() snapshot in your app into a Prometheus scrape target through one Elysia plugin.
Prometheus / OpenMetrics exposure for Bun and Elysia apps. Every AbsoluteJS package already exposes a typed metrics() snapshot; this package standardizes those shapes into MetricSample[], renders Prometheus text format and mounts a single GET /metrics via an Elysia plugin — no hand-rolled endpoint per service. Anything that scrapes Prometheus text (Prometheus, VictoriaMetrics, Grafana Agent, OTLP collectors) can consume it, and counter/gauge helpers cover your own app metrics.
bun add @absolutejs/metricsPrometheus counters, gauges, cumulative histogram families, collectors, and an authenticated Elysia exposition plugin for AbsoluteJS services.
Prometheus / OpenMetrics exposure for the AbsoluteJS substrate. Every substrate package already exposes a typed metrics() snapshot — this package converts those snapshots into the scrape format Prometheus, VictoriaMetrics, Grafana Agent, OTLP collectors, etc. all understand.
The substrate ships instrumentation but no exposure path. Operators wire one metricsPlugin() and every metrics() shape across the substrate becomes a scrape target — no hand-rolled /metrics per service.
On a listener reachable outside a trusted scrape network, authorize before any collector runs:
Output looks like:
Each substrate package gets its own subpath import:
Subpath — Source
@absolutejs/metrics/runtime — @absolutejs/runtime
@absolutejs/metrics/router — @absolutejs/router
@absolutejs/metrics/egress — runtime egress guard
@absolutejs/metrics/queue — @absolutejs/queue
@absolutejs/metrics/sync — @absolutejs/sync engine
@absolutejs/metrics/secrets — @absolutejs/secrets broker
@absolutejs/metrics/rate-limit — @absolutejs/rate-limit
@absolutejs/metrics/audit — @absolutejs/audit
@absolutejs/metrics/dispatch — @absolutejs/dispatch
@absolutejs/metrics/errors — @absolutejs/errors tracker
@absolutejs/metrics/logs — @absolutejs/logs logger
Convention: abs__ for substrate metrics (abs_runtime_active, abs_queue_completed_total). Your app's metrics should use _ so they don't collide.
Counters end in _total. Gauges don't. (Per Prometheus naming conventions.)
metricsPlugin({ registry }) mounts GET /metrics on any Elysia app and serves Prometheus text format; elysia is an optional peer needed only for the plugin.
Subpath collectors (runtime, queue, sync, secrets, rate-limit, audit, dispatch) adapt each substrate package metrics() shape; each takes a plain snapshot function, so the source packages are never hard dependencies.
counter() and gauge() build MetricSample values with help text and labels, so app-specific metrics register alongside substrate ones.
Substrate metrics follow abs_<source>_<metric> with counters ending in _total, matching Prometheus naming conventions and avoiding collisions with your app metrics.
Outcomes
Prometheus counters, gauges, cumulative histogram families, collectors, and an authenticated Elysia exposition plugin for AbsoluteJS services.
The substrate ships instrumentation but no exposure path. Operators wire one metricsPlugin() and every metrics() shape across the substrate becomes a scrape target — no hand-rolled /metrics per service.
elysia is an optional peer dep (only needed for metricsPlugin).
Hardening checklist
Follow in order
Working example for Usage.
import { Elysia } from 'elysia';
import { createMetricsRegistry, metricsPlugin } from '@absolutejs/metrics';
import { runtimeCollector } from '@absolutejs/metrics/runtime';
import { routerCollector } from '@absolutejs/metrics/router';
import { egressCollector } from '@absolutejs/metrics/egress';
import {
queueCollector,
wakeSchedulerCollector
} from '@absolutejs/metrics/queue';
import { syncCollector } from '@absolutejs/metrics/sync';
import { secretsCollector } from '@absolutejs/metrics/secrets';
import { auditCollector } from '@absolutejs/metrics/audit';
import { dispatchCollector } from '@absolutejs/metrics/dispatch';
import { errorsCollector } from '@absolutejs/metrics/errors';
import { logsCollector } from '@absolutejs/metrics/logs';
const registry = createMetricsRegistry();
registry.register(
'runtime',
runtimeCollector(() => runtime.metrics())
);
registry.register(
'router',
routerCollector(() => router.metrics())
);
registry.register(
'egress',
egressCollector(() => egressGuard.metrics())
);
registry.register(
'queue',
queueCollector(() => worker.metrics())
);
registry.register(
'billing-wakes',
wakeSchedulerCollector(() => billingScheduler.metrics(), {
labels: { scheduler: 'billing' }
})
);
registry.register(
'sync',
syncCollector(() => engine.metrics())
);
registry.register(
'secrets',
secretsCollector(() => broker.metrics())
);
registry.register(
'audit',
auditCollector(() => audit.metrics())
);
registry.register(
'dispatch',
dispatchCollector(() => dispatcher.metrics())
);
registry.register(
'errors',
errorsCollector(() => tracker.metrics())
);
registry.register(
'logs',
logsCollector(() => logger.metrics())
);
const app = new Elysia().use(await metricsPlugin({ registry }));
// GET /metrics → Prometheus textOn a listener reachable outside a trusted scrape network, authorize before any collector runs:
const app = new Elysia().use(
await metricsPlugin({
registry,
authorize: (request) =>
request.headers.get('authorization') === `Bearer ${scrapeToken}`
})
);Expose your own counters and gauges next to the substrate metrics.
import { counter, gauge } from '@absolutejs/metrics';
registry.register('app', () => [
counter('myapp_requests_total', requestCount, {
help: 'Total HTTP requests',
labels: { route: '/api/users' }
}),
gauge('myapp_workers', activeWorkers, {
help: 'Currently running workers'
})
]);Register a collector per source and mount the scrape endpoint with one plugin.
import { createMetricsRegistry, metricsPlugin } from '@absolutejs/metrics';
import { auditCollector } from '@absolutejs/metrics/audit';
import { queueCollector } from '@absolutejs/metrics/queue';
import { runtimeCollector } from '@absolutejs/metrics/runtime';
import { syncCollector } from '@absolutejs/metrics/sync';
import { Elysia } from 'elysia';
const registry = createMetricsRegistry();
registry.register('audit', auditCollector(() => audit.metrics()));
registry.register('queue', queueCollector(() => worker.metrics()));
registry.register('runtime', runtimeCollector(() => runtime.metrics()));
registry.register('sync', syncCollector(() => engine.metrics()));
const app = new Elysia().use(await metricsPlugin({ registry }));
// GET /metrics → Prometheus textCurrent package surface
Import surface · click to copy