{-# LANGUAGE TupleSections #-} module System.Metrics.Prometheus.Metric.Histogram ( Histogram, HistogramSample (..), Buckets, UpperBound, new, observe, sample, observeAndSample, ) where import Control.Applicative ((<$>)) import Control.Monad (void) import Data.Bool (bool) import Data.IORef ( IORef, atomicModifyIORef', newIORef, readIORef, ) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map newtype Histogram = Histogram {unHistogram :: IORef HistogramSample} type UpperBound = Double -- Inclusive upper bounds type Buckets = Map UpperBound Double data HistogramSample = HistogramSample { histBuckets :: !Buckets , histSum :: !Double , histCount :: !Int } new :: [UpperBound] -> IO Histogram new buckets = Histogram <$> newIORef empty where empty = HistogramSample (Map.fromList $ map (,0) (read "Infinity" : buckets)) zeroSum zeroCount zeroSum = 0.0 zeroCount = 0 observeAndSample :: Double -> Histogram -> IO HistogramSample observeAndSample x = flip atomicModifyIORef' update . unHistogram where update histData = (hist' histData, histData) hist' histData = histData { histBuckets = updateBuckets x $ histBuckets histData , histSum = histSum histData + x , histCount = histCount histData + 1 } observe :: Double -> Histogram -> IO () observe x = void . observeAndSample x updateBuckets :: Double -> Buckets -> Buckets updateBuckets x = Map.mapWithKey updateBucket where updateBucket key val = bool val (val + 1) (x <= key) sample :: Histogram -> IO HistogramSample sample = readIORef . unHistogram