atelier-core-0.1.0.0: src/Atelier/Effects/Monitoring/Metrics/Registry.hs
-- | Metric registry for managing Prometheus metrics.
--
-- The actual implementation of the `runMetrics` handler.
module Atelier.Effects.Monitoring.Metrics.Registry
( MetricHandles
, initMetricHandles
, setGauge
, incGauge
, decGauge
, incCounter
, addCounter
, observeHistogram
) where
import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef)
import Data.Map.Strict qualified as Map
import Prometheus qualified as Prom
-- | Mutable handles to the registered Prometheus metrics, keyed by name and
-- created on first use.
data MetricHandles = MetricHandles
{ gauges :: IORef (Map Text Prom.Gauge)
, counters :: IORef (Map Text Prom.Counter)
, histograms :: IORef (Map Text Prom.Histogram)
}
-- | Initialize empty metric handles
initMetricHandles :: IO MetricHandles
initMetricHandles = do
MetricHandles
<$> newIORef mempty
<*> newIORef mempty
<*> newIORef mempty
-- | Set a gauge to a specific value
setGauge :: MetricHandles -> Text -> Double -> IO ()
setGauge handles name value = do
g <- getOrCreateGauge handles name
Prom.setGauge g value
-- | Increment a gauge by 1
incGauge :: MetricHandles -> Text -> IO ()
incGauge handles name = do
g <- getOrCreateGauge handles name
Prom.incGauge g
-- | Decrement a gauge by 1
decGauge :: MetricHandles -> Text -> IO ()
decGauge handles name = do
g <- getOrCreateGauge handles name
Prom.decGauge g
-- | Increment a counter by 1
incCounter :: MetricHandles -> Text -> IO ()
incCounter handles name = do
c <- getOrCreateCounter handles name
Prom.incCounter c
-- | Add a value to a counter
addCounter :: MetricHandles -> Text -> Double -> IO ()
addCounter handles name value = do
c <- getOrCreateCounter handles name
void $ Prom.addCounter c value
-- | Observe a value in a histogram
observeHistogram :: MetricHandles -> Text -> Double -> IO ()
observeHistogram handles name value = do
h <- getOrCreateHistogram handles name
Prom.observe h value
-- | Get or create a gauge metric
getOrCreateGauge :: MetricHandles -> Text -> IO Prom.Gauge
getOrCreateGauge handles name = do
gaugeMap <- readIORef handles.gauges
case Map.lookup name gaugeMap of
Just g -> pure g
Nothing -> do
g <- Prom.register $ Prom.gauge (Prom.Info name "")
atomicModifyIORef' handles.gauges $ \m ->
(Map.insert name g m, ())
pure g
-- | Get or create a counter metric
getOrCreateCounter :: MetricHandles -> Text -> IO Prom.Counter
getOrCreateCounter handles name = do
counterMap <- readIORef handles.counters
case Map.lookup name counterMap of
Just c -> pure c
Nothing -> do
c <- Prom.register $ Prom.counter (Prom.Info name "")
atomicModifyIORef' handles.counters $ \m ->
(Map.insert name c m, ())
pure c
-- | Get or create a histogram metric
getOrCreateHistogram :: MetricHandles -> Text -> IO Prom.Histogram
getOrCreateHistogram handles name = do
histogramMap <- readIORef handles.histograms
case Map.lookup name histogramMap of
Just h -> pure h
Nothing -> do
-- Default buckets for duration metrics: 1ms, 10ms, 100ms, 1s, 10s
let buckets = [0.001, 0.01, 0.1, 1.0, 10.0]
h <- Prom.register $ Prom.histogram (Prom.Info name "") buckets
atomicModifyIORef' handles.histograms $ \m ->
(Map.insert name h m, ())
pure h