module Prometheus.Metric.Counter (
Counter
, counter
, incCounter
, addCounter
, unsafeAddCounter
, addDurationToCounter
, getCounter
, countExceptions
) where
import Prometheus.Info
import Prometheus.Metric
import Prometheus.Metric.Observer (timeAction)
import Prometheus.MonadMonitor
import Control.DeepSeq
import Control.Monad.Catch
import Control.Monad.IO.Class
import Control.Monad (unless)
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)
instance NFData Counter where
rnf (MkCounter ioref) = seq ioref ()
counter :: Info -> Metric Counter
counter info = Metric $ do
ioref <- IORef.newIORef 0
return (MkCounter ioref, collectCounter info ioref)
withCounter :: MonadMonitor m
=> Counter
-> (Double -> Double)
-> m ()
withCounter (MkCounter ioref) f =
doIO $ Atomics.atomicModifyIORefCAS_ ioref f
incCounter :: MonadMonitor m => Counter -> m ()
incCounter c = withCounter c (+ 1)
addCounter :: MonadMonitor m => Counter -> Double -> m Bool
addCounter c x
| x < 0 = return False
| otherwise = do
withCounter c add
return True
where add i = i `seq` x `seq` i + x
unsafeAddCounter :: MonadMonitor m => Counter -> Double -> m ()
unsafeAddCounter c x = do
added <- addCounter c x
unless added $
error $ "Tried to add negative value to counter: " ++ show x
addDurationToCounter :: (MonadIO m, MonadMonitor m) => Counter -> m a -> m a
addDurationToCounter metric io = do
(result, duration) <- timeAction io
_ <- addCounter metric duration
return result
getCounter :: MonadIO m => Counter -> m Double
getCounter (MkCounter ioref) = liftIO $ 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]]
countExceptions :: (MonadCatch m, MonadMonitor m) => Counter -> m a -> m a
countExceptions m io = io `onException` incCounter m