Zum Inhalt springen
Deutsch

Prometheus-Exporter

Der Prometheus-Exporter verwandelt die In-Process- Metriken-Registry in das Prometheus-Textformat, das Prometheus / Grafana / Thanos von /metrics scrapen.

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

Gibt einen einzelnen String im Prometheus-0.0.4-Exposition-Format zurück:

# 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

Der einfachste Weg führt über die Management-Routen:

import { managementRoutes } from 'actor-ts/management';
// cluster can be null when you only need the metrics endpoint
const routes = managementRoutes(system, null, {
enableMetricsEndpoint: true, // ← exposes GET /metrics
});
await system.http(8558).bind(routes);

Prometheus-Scrape-Config:

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

Der Management-Server läuft auf einem separaten Port vom Haupt-HTTP-Server deiner App. Typisches Muster: App auf 8080 (öffentlich), Management auf 8558 (nur intern).

Diese Route liest die Registry über collect() und braucht deshalb eine Registry, die sich so lesen lässt — die des Frameworks kann das. Die prom-client-Bridge kann es nicht: sie leitet ihre Schreibvorgänge weiter und behält keinen Snapshot. Mit dieser Bridge antwortet die Route mit 503 und benennt den Konflikt, statt mit 200 und leerem Body, den Prometheus als gesundes Target ohne Serien akzeptieren würde. Scrape in diesem Setup die eigene /metrics-Route von prom-client und lass enableMetricsEndpoint aus.

Wenn du die Management-Routen nicht nutzt, route GET /metrics selbst:

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

Binde wenn möglich an einen dedizierten Port — Prometheus’ Scrapes sind periodisch und sollten den App-Port nicht teilen.

http_requests_total # Counter — Suffix `_total`
sessions_active # Gauge — kein Suffix
http_request_duration_seconds # Histogram — _bucket / _count / _sum
process_cpu_seconds_total # Counter — Prometheus-Namensgebung

Prometheus hat Konventionen (Naming-Guide):

  • Counter enden auf _total (oder beschreiben, was gezählt wird).
  • Histogramme / Timer enthalten die Einheit im Namen (_seconds, _ms, _bytes).
  • _count / _sum sind vom Framework angehängte Suffixe für Histogramme.
  • Labels sind snake_case-Strings.

Das Framework erzwingt diese nicht — aber Prometheus-Tooling erwartet sie.

  • Scrape alle 15-30 Sekunden — Prometheus-Default.
  • Gauges in beliebiger Kadenz aktualisieren — Prometheus sieht nur den Wert zur Scrape-Zeit.
  • Counter können in beliebiger Rate inkrementiert werden; Prometheus berechnet die Rate aus aufeinanderfolgenden Scrapes.

Für sehr schnell ändernde Werte (Speichernutzung, Mailbox-Tiefe) kann der Gauge zur Scrape-Zeit veraltet sein. Das ist okay — Prometheus ist mit seinen Samples eventually consistent.

SerieFormat
CounterEine Zeile pro Serie. metric_name{labels} value
GaugeGleich wie Counter.
HistogramEine _bucket{le=N}-Zeile pro Bucket + _count + _sum.

Der Exporter übernimmt die Formatierung; du baust sie nicht manuell.

Die exportPrometheus- Funktion-Referenz deckt die volle API ab.