module Prometheus.Metric.Counter (
Counter
, counter
, incCounter
, addCounter
, unsafeAddCounter
, addDurationToCounter
, getCounter
) where
import Prometheus.Info
import Prometheus.Metric
import Prometheus.MonadMonitor
import Control.Monad (unless)
import Data.Time.Clock (diffUTCTime, getCurrentTime)
import qualified Data.Atomics as Atomics
import qualified Data.ByteString.UTF8 as BS
import qualified Data.IORef as IORef
newtype Counter = MkCounter (IORef.IORef Double)
counter :: Info -> IO (Metric Counter)
counter info = do
ioref <- IORef.newIORef 0
return Metric {
handle = MkCounter ioref
, collect = collectCounter info ioref
}
withCounter :: MonadMonitor m
=> Metric Counter
-> (Double -> Double)
-> m ()
withCounter Metric {handle = MkCounter ioref} f =
doIO $ Atomics.atomicModifyIORefCAS_ ioref f
incCounter :: MonadMonitor m => Metric Counter -> m ()
incCounter c = withCounter c (+ 1)
addCounter :: MonadMonitor m => Double -> Metric Counter -> m Bool
addCounter x c
| x < 0 = return False
| otherwise = do
withCounter c add
return True
where add i = i `seq` x `seq` i + x
unsafeAddCounter :: MonadMonitor m => Double -> Metric Counter -> m ()
unsafeAddCounter x c = do
added <- addCounter x c
unless added $
error $ "Tried to add negative value to counter: " ++ show x
addDurationToCounter :: IO a -> Metric Counter -> IO a
addDurationToCounter io metric = do
start <- getCurrentTime
result <- io
end <- getCurrentTime
addCounter (fromRational $ toRational $ end `diffUTCTime` start) metric
return result
getCounter :: Metric Counter -> IO Double
getCounter Metric {handle = MkCounter ioref} = IORef.readIORef ioref
collectCounter :: Info -> IORef.IORef Double -> IO [SampleGroup]
collectCounter info c = do
value <- IORef.readIORef c
let sample = Sample (metricName info) [] (BS.fromString $ show value)
return [SampleGroup info CounterType [sample]]