Prometheus Metrics
Rift exposes two metrics surfaces:
- A Prometheus endpoint (
GET /metricson the metrics port, default9090) with the full instrumentation described below. - A small plain-text counter set on the admin API’s own
GET /metrics(admin port, default2525) — imposter counts and per-imposter request totals.
Every metric name and label on this page is taken from the source; if a metric isn’t listed here, it isn’t emitted.
Enabling & scraping
The metrics server starts alongside the admin server. Point Prometheus at the metrics port:
curl http://localhost:9090/metrics
Change the port with --metrics-port or RIFT_METRICS_PORT:
rift-http-proxy --metrics-port 8090
# or
RIFT_METRICS_PORT=8090 rift-http-proxy
There is no environment variable to disable the metrics server; if you don’t scrape it, it sits idle. (An imposter’s _rift.metrics block controls per-imposter metric emission — see Rift Extensions.)
Prometheus endpoint metrics (port 9090)
Histogram metrics expand into the usual _bucket{le="…"}, _sum, and _count series.
| Metric | Type | Labels | Meaning |
|---|---|---|---|
rift_requests_total | counter | method, status | Requests served, by method and response status. |
rift_faults_injected_total | counter | type, rule_id, source | Faults injected (type = latency/error/tcp). |
rift_latency_injected_ms | histogram | rule_id | Injected latency, in milliseconds. |
rift_error_status_total | counter | status, rule_id | Error-fault responses, by status. |
rift_script_execution_duration_ms | histogram | rule_id, result | Script execution time, in milliseconds. |
rift_script_errors_total | counter | rule_id, error_type | Script failures, by error type. |
rift_flow_state_ops_total | counter | operation, result | Flow-store operations (get/set/…), by result. |
rift_active_flows | gauge | backend | Currently-tracked flows, by backend. |
rift_proxy_request_duration_ms | histogram | method, fault_applied | Proxy handling time, in milliseconds. |
rift_upstream_request_duration_ms | histogram | method, status | Upstream (proxied) request time, in milliseconds. |
rift_accepted_connections_total | counter | worker | Connections accepted per accept-loop worker slot. Under --runtime per-core the slot is the worker index, making SO_REUSEPORT skew observable; in the default topology everything lands on slot 0. |
rift_accept_errors_total | counter | listener, class | Accept errors by listener (imposter, admin, metrics, proxy) and class (transient, systemic). The accept loops classify and retry rather than terminate, so a listener can stay bound while unable to serve — this is the signal that it is degraded. Fatal (broken-fd) errors are excluded on the admin, metrics and proxy listeners — they end the loop and surface through its owner. The imposter loops have no fatal class by design (a dying imposter loop is recoverable through the still-live admin API), so there a broken fd counts as systemic. No port label, deliberately (unbounded cardinality); the log lines carry the port. Present at 0 from startup for every running listener, so rate()/increase() behave on the first error. |
rift_accept_error_outage | gauge | listener | 1 while any accept loop for that listener is in a systemic outage, 0 otherwise — the imposter plane runs many loops behind one label, so this counts depth rather than tracking whichever loop wrote last. Released when a loop ends while wedged, so it cannot stick at 1. Present at 0 from startup, so absent() alerts work. The one to alert on. |
Example scrape output:
rift_requests_total{method="GET",status="200"} 1234
rift_faults_injected_total{type="latency",rule_id="api-latency",source="rift"} 300
rift_latency_injected_ms_bucket{rule_id="api-latency",le="100"} 120
rift_latency_injected_ms_sum{rule_id="api-latency"} 45670
rift_latency_injected_ms_count{rule_id="api-latency"} 300
rift_flow_state_ops_total{operation="get",result="ok"} 5000
rift_active_flows{backend="inmemory"} 12
Admin GET /metrics (port 2525)
The admin API serves a minimal hand-written counter set (Content-Type: text/plain; version=0.0.4):
| Metric | Type | Labels | Meaning |
|---|---|---|---|
rift_imposters_total | gauge | — | Number of imposters currently registered. |
rift_imposter_requests_total | counter | port | Requests per imposter (one line per port). |
rift_imposters_total 5
rift_imposter_requests_total{port="4545"} 500
rift_imposter_requests_total{port="4546"} 128
Prometheus configuration
# prometheus.yml
scrape_configs:
- job_name: 'rift'
static_configs:
- targets: ['localhost:9090']
scrape_interval: 15s
Kubernetes service discovery
scrape_configs:
- job_name: 'rift'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: rift
action: keep
Useful queries
Request rate by status:
sum(rate(rift_requests_total[5m])) by (status)
5xx error ratio:
sum(rate(rift_requests_total{status=~"5.."}[5m]))
/
sum(rate(rift_requests_total[5m]))
Fault injection rate by type:
sum(rate(rift_faults_injected_total[5m])) by (type)
P99 upstream latency (proxy mode):
histogram_quantile(0.99, sum(rate(rift_upstream_request_duration_ms_bucket[5m])) by (le))
Script error rate:
sum(rate(rift_script_errors_total[5m])) by (error_type)
Alerting rules
groups:
- name: rift
rules:
- alert: RiftHighErrorRate
expr: |
sum(rate(rift_requests_total{status=~"5.."}[5m]))
/
sum(rate(rift_requests_total[5m])) > 0.05
for: 5m
labels: { severity: warning }
annotations:
summary: "High 5xx rate in Rift"
- alert: RiftScriptErrors
expr: rate(rift_script_errors_total[5m]) > 0
for: 1m
labels: { severity: critical }
annotations:
summary: "Script errors in Rift"
description: "error_type="
Best practices
- Reasonable scrape intervals — 15–30s is typical.
- Use recording rules for expensive dashboard queries.
- Alert on error ratio and fault rate, not raw counts.
- Mind label cardinality —
rule_idis bounded by your config; avoid adding unbounded labels.