Turn 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.
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.
Why
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.
Usage
On a listener reachable outside a trusted scrape network, authorize before any collector runs:
Output looks like:
Collectors
Each substrate package gets its own subpath import:
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.)
One-line /metrics endpoint
metricsPlugin({ registry }) mounts GET /metrics on any Elysia app and serves Prometheus text format; elysia is an optional peer needed only for the plugin.
Per-source collectors
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.
Custom metric helpers
counter() and gauge() build MetricSample values with help text and labels, so app-specific metrics register alongside substrate ones.
Consistent naming
Substrate metrics follow abs_<source>_<metric> with counters ending in _total, matching Prometheus naming conventions and avoiding collisions with your app metrics.
Outcomes
What you can build
Overview
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.
Why
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.
Install
elysia is an optional peer dep (only needed for metricsPlugin).
Hardening checklist
Production guidance
Make every external boundary explicitPin the deployed @absolutejs/metrics version, replace example or memory-backed dependencies with durable implementations, bound external calls, protect credentials, and emit enough evidence to retry or recover safely.
Follow in order
Troubleshooting path
1
Trace from the first failed boundary
Reproduce the smallest canonical @absolutejs/metrics example, confirm the supported entry point and version in the API explorer, then inspect the first boundary that did not produce its documented result.
Register a collector per source and mount the scrape endpoint with one plugin.
TS
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 text
Loose coupling by design
Collectors rely on TypeScript structural typing: pass () => instance.metrics() from @absolutejs/runtime, queue, sync, secrets, rate-limit, audit or dispatch and the shapes line up without importing those packages here.
@absolutejs/metrics — Prometheus / OpenMetrics exposure for the AbsoluteJS substrate. The substrate already wears its instrumentation on its sleeve: every package exposes a metrics() method returning a typed snapshot. What's missing is the last mile — converting those shapes into the Prometheus text format so scrapers can read them. Shape: 1. MetricSample is the small intermediate format: a { name, value, type, help?, labels? } shape that maps cleanly to Prometheus / OpenMetrics output. 2. Metri
TS
type MetricType = 'counter' | 'gauge' | 'histogram' | 'untyped';