Skip to content

Prometheus exporter

The Prometheus exporter turns the in-process metrics registry into the Prometheus text format that Prometheus / Grafana / Thanos scrape from /metrics.

import { ActorSystem, MetricsExtensionId, exportPrometheus } from 'actor-ts';
const system = ActorSystem.create('my-app');
const metrics = system.extension(MetricsExtensionId);
// Inside an HTTP handler:
get(async () => {
const text = exportPrometheus(metrics.registry);
return { status: 200, body: text, contentType: 'text/plain; version=0.0.4' };
});

Returns a single string in Prometheus 0.0.4 exposition format:

# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{route="/orders"} 1234
http_requests_total{route="/users"} 567
# HELP sessions_active Currently active sessions
# TYPE sessions_active gauge
sessions_active 123
# HELP http_request_duration_ms HTTP request latency
# TYPE http_request_duration_ms histogram
http_request_duration_ms_bucket{route="/orders",le="10"} 100
http_request_duration_ms_bucket{route="/orders",le="50"} 250
http_request_duration_ms_bucket{route="/orders",le="+Inf"} 300
http_request_duration_ms_count{route="/orders"} 300
http_request_duration_ms_sum{route="/orders"} 12450

The easiest path is via the HttpManagement helper:

import { HttpManagement } from 'actor-ts';
await HttpManagement.start(system, {
port: 8558,
enableMetricsEndpoint: true, // ← exposes GET /metrics
});

Prometheus scrape config:

scrape_configs:
- job_name: 'actor-ts'
static_configs:
- targets: ['my-app:8558']

The management server runs on a separate port from your app’s main HTTP server. Typical pattern: app on 8080 (public), management on 8558 (internal-only).

If you’re not using HttpManagement, route a GET /metrics yourself:

import { path, get } from 'actor-ts/http';
import { exportPrometheus } from 'actor-ts';
const routes = path('metrics',
get(async () => ({
status: 200,
body: exportPrometheus(metrics.registry),
contentType: 'text/plain; version=0.0.4',
headers: {},
})),
);

Bind on a dedicated port if you can — Prometheus’s scrapes are periodic and shouldn’t share the app port.

http_requests_total # counter — `_total` suffix
sessions_active # gauge — no suffix
http_request_duration_seconds # histogram — _bucket / _count / _sum
process_cpu_seconds_total # counter again — Prometheus naming

Prometheus has conventions (naming guide):

  • Counters end in _total (or describe what’s counted).
  • Histograms / timers include the unit in the name (_seconds, _ms, _bytes).
  • _count / _sum are framework-appended suffixes for histograms.
  • Labels are snake_case strings.

The framework doesn’t enforce these — but Prometheus tooling expects them.

  • Scrape every 15-30 seconds — Prometheus default.
  • Update gauges at any cadence — Prometheus only sees the value at scrape time.
  • Counters can be incremented at any rate; Prometheus computes the rate from successive scrapes.

For very-fast-changing values (memory usage, mailbox depth), the gauge may be stale by the time it’s scraped. That’s fine — Prometheus is eventually consistent with its samples.

SeriesFormat
CounterOne line per series. metric_name{labels} value
GaugeSame as counter.
HistogramOne _bucket{le=N} line per bucket + _count + _sum.

The exporter handles the formatting; you don’t construct it manually.

The exportPrometheus function reference covers the full API.