ekg and prometheus-haskell: ensuring metric compatibility for Prometheus alerting
0 reputation · 30 Apr 2025, 10:05 UTC
0 reputation · 30 Apr 2025, 10:05 UTC
The goal is to instrument a Haskell application with ekg for custom metric collection and use prometheus‑haskell to expose those metrics through an HTTP /metrics endpoint so that Prometheus can evaluate alert rules.
It is unclear whether ekg’s counters, gauges, and histograms are automatically picked up by the prometheus‑haskell registry or must be manually registered, how metric labels are preserved across the two libraries, and what configuration prevents duplicate exposition or unnecessary runtime overhead.
What steps are required to align ekg metric names with Prometheus exposition format without manual duplication? Which labeling strategy ensures that Prometheus alert rules can reference the original ekg dimensions? Does automatic registration introduce measurable latency in high‑frequency code paths?
32275 reputation · 30 Apr 2025, 16:26 UTC
ekg and prometheus-haskell do not share a registry. Metrics created with System.Metrics live in ekg's internal store and will never appear on a prometheus-haskell /metrics endpoint by themselves. The working pattern is a bridge that samples ekg and mirrors values into a prometheus-haskell registry — either on a timer or lazily at scrape time.
Confirmed behavior: ekg keeps its own store; System.Metrics.sampleAll returns every metric name and current value; prometheus-haskell exposes only what is explicitly registered in its Registry. Assumed (verify against your pinned versions): exact module paths and helper functions differ between releases of both libraries, so treat the sketch below as architecture, not copy-paste code.
[a-zA-Z_:][a-zA-Z0-9_:]*. Map dots and dashes to underscores (e.g. app.requests.total → app_requests_total), then enumerate all ekg metrics via sampleAll and assert the mapping is injective — two distinct ekg names collapsing to one Prometheus name is a real collision risk.sampleAll and push values into the registered metrics. This avoids any per-update registration cost.-- sketch, not compilable as-is
bridge store registry = forever $ do
sample <- sampleAll store
forM_ (HashMap.toList sample) $ \(name, val) ->
updateMirrored registry (sanitize name) val
threadDelay 5000000 -- 5s, well under a typical 15s scrape intervalekg has no first-class label concept — dimensions are usually baked into the name (db.pool.read.queries). To make Prometheus alert rules reference those dimensions, your bridge must parse the ekg name into (base name, label set) and register labeled metrics explicitly:
-- "db.pool.read.queries" ->
-- db_pool_queries_total{pool="read"}Keep the parsing deterministic and total: any ekg name that fails to parse should fall back to a sanitized unlabeled metric rather than being dropped, or alerts will silently see nothing. Write the alert rule against the labeled form and trigger it in a test environment before relying on it.
ekg histograms and Prometheus histograms are not the same abstraction — bucket layout and quantile semantics differ. Copying raw values naively produces misleading histogram_quantile results. Prefer re-recording observations directly into a prometheus-haskell histogram at the instrumentation site, or expose ekg distribution summaries as gauges (min/max/mean) and alert on those instead.
ekg counter/gauge updates are cheap atomic operations; the bridge cost scales with total metric count × sampling frequency, not with the hot code path. So automatic mirroring does not add measurable latency to high-frequency instrumentation — but verify rather than assume: benchmark the hot path with and without ekg instrumentation, and time one sampleAll pass at your production metric count. If you have tens of thousands of metrics, lower the sampling frequency rather than the scrape fidelity.
/metrics, confirm the sanitized name appears exactly once.One detail that would change this advice: if your ekg metric set is dynamic (names appear at runtime), the pre-registration step needs a reconciliation loop — say so and the design shifts accordingly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.