module Control.Monad.Metrics
(
MonadMetrics(..)
, Metrics
, initialize
, initializeWith
, increment
, counter
, counter'
, gauge
, gauge'
, distribution
, timed
, timed'
, label
, label'
, Resolution(..)
) where
import Control.Monad (liftM)
import Data.Monoid (mempty)
import Control.Monad.IO.Class
import Data.IORef
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as Text
import System.Clock (Clock (..), TimeSpec (..),
getTime)
import qualified System.Metrics as EKG
import System.Metrics.Counter as Counter
import System.Metrics.Distribution as Distribution
import System.Metrics.Gauge as Gauge
import System.Metrics.Label as Label
import Prelude
import Control.Monad.Metrics.Internal
class MonadMetrics m where
getMetrics :: m Metrics
initializeWith :: EKG.Store -> IO Metrics
initializeWith metricsStore = do
metricsCounters <- newIORef mempty
metricsDistributions <- newIORef mempty
metricsGauges <- newIORef mempty
metricsLabels <- newIORef mempty
return Metrics{..}
initialize :: IO Metrics
initialize = EKG.newStore >>= initializeWith
increment :: (MonadIO m, MonadMetrics m) => Text -> m ()
increment name = counter name 1
counter' :: (MonadIO m, MonadMetrics m, Integral int) => Text -> int -> m ()
counter' =
modifyMetric Counter.add fromIntegral EKG.createCounter metricsCounters
counter :: (MonadIO m, MonadMetrics m) => Text -> Int -> m ()
counter = counter'
distribution :: (MonadIO m, MonadMetrics m) => Text -> Double -> m ()
distribution =
modifyMetric Distribution.add id EKG.createDistribution metricsDistributions
gauge' :: (MonadIO m, MonadMetrics m, Integral int) => Text -> int -> m ()
gauge' =
modifyMetric Gauge.set fromIntegral EKG.createGauge metricsGauges
gauge :: (MonadIO m, MonadMetrics m) => Text -> Int -> m ()
gauge = gauge'
timed' :: (MonadIO m, MonadMetrics m) => Resolution -> Text -> m a -> m a
timed' resolution name action = do
start <- liftIO $ getTime Monotonic
result <- action
end <- liftIO $ getTime Monotonic
distribution name (diffTime resolution start end)
return result
timed :: (MonadIO m, MonadMetrics m) => Text -> m a -> m a
timed = timed' Seconds
label :: (MonadIO m, MonadMetrics m) => Text -> Text -> m ()
label = modifyMetric Label.set id EKG.createLabel metricsLabels
label' :: (MonadIO m, MonadMetrics m, Show a) => Text -> a -> m ()
label' l = label l . Text.pack . show
diffTime :: Resolution -> TimeSpec -> TimeSpec -> Double
diffTime res (TimeSpec seca nseca) (TimeSpec secb nsecb) =
let sec = seca secb
nsec = nseca nsecb
in convertTimeSpecTo res (TimeSpec sec nsec)
convertTimeSpecTo :: Resolution -> TimeSpec -> Double
convertTimeSpecTo res (TimeSpec secs' nsecs') =
case res of
Nanoseconds -> nsecs + sToNs secs
Microseconds -> nsToUs nsecs + sToUs secs
Milliseconds -> nsToMs nsecs + sToMs secs
Seconds -> nsToS nsecs + secs
Minutes -> sToMin (nsToS nsecs + secs)
Hours -> sToHour (nsToS nsecs + secs)
Days -> sToDay (nsToS nsecs + secs)
where
nsecs = fromIntegral nsecs'
secs = fromIntegral secs'
nsToUs, nsToMs, nsToS, sToMin, sToHour, sToDay, sToNs, sToUs, sToMs :: Double -> Double
nsToUs = (/ 10^3)
nsToMs = (/ 10^6)
nsToS = (/ 10^9)
sToMin = (/ 60)
sToHour = sToMin . sToMin
sToDay = (/ 24) . sToHour
sToNs = (* 10^9)
sToUs = (* 10^6)
sToMs = (* 10^3)
modifyMetric
:: (MonadMetrics m, MonadIO m)
=> (t -> t1 -> IO b)
-> (t2 -> t1)
-> (Text -> EKG.Store -> IO t)
-> (Metrics -> IORef (Map Text t))
-> Text
-> t2
-> m b
modifyMetric adder converter creator getter name value = do
bar <- lookupOrCreate getter creator name
liftIO $ adder bar (converter value)
lookupOrCreate
:: (MonadMetrics m, MonadIO m, Ord k)
=> (Metrics -> IORef (Map k a)) -> (k -> EKG.Store -> IO a) -> k -> m a
lookupOrCreate getter creator name = do
ref <- liftM getter getMetrics
container <- liftIO $ readIORef ref
case Map.lookup name container of
Nothing -> do
c <- liftIO . creator name =<< liftM metricsStore getMetrics
liftIO $ modifyIORef ref (Map.insert name c)
return c
Just c -> return c