metrics-0.4.1.0: src/Data/Metrics/Meter/Internal.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Metrics.Meter.Internal (
Meter,
meterData,
mark,
clear,
tick,
meanRate,
oneMinuteAverage,
fiveMinuteAverage,
fifteenMinuteAverage,
tickIfNecessary,
count,
lastTick
) where
import Control.Lens
import Control.Lens.TH
import Data.Time.Clock
import qualified Data.Metrics.MovingAverage as M
data Meter = Meter
{ meterCount :: {-# UNPACK #-} !Int
, meterOneMinuteRate :: !M.MovingAverage
, meterFiveMinuteRate :: !M.MovingAverage
, meterFifteenMinuteRate :: !M.MovingAverage
, meterStartTime :: !NominalDiffTime
, meterLastTick :: !NominalDiffTime
, meterTickInterval :: {-# UNPACK #-} !Double
} deriving (Show)
makeFields ''Meter
meterData :: Double -> (Double -> Int -> M.MovingAverage) -> NominalDiffTime -> Meter
meterData ti f t = Meter
{ meterCount = 0
, meterOneMinuteRate = f ti 1
, meterFiveMinuteRate = f ti 5
, meterFifteenMinuteRate = f ti 15
, meterStartTime = t
, meterLastTick = t
, meterTickInterval = ti
}
-- TODO: make moving average prism
mark :: NominalDiffTime -> Int -> Meter -> Meter
mark t c m = ticked
& count +~ c
& oneMinuteRate %~ updateMeter
& fiveMinuteRate %~ updateMeter
& fifteenMinuteRate %~ updateMeter
where
updateMeter = M.update $ fromIntegral c
ticked = tickIfNecessary t m
{-# INLINEABLE mark #-}
clear :: NominalDiffTime -> Meter -> Meter
clear t =
(count .~ 0) .
(startTime .~ t) .
(lastTick .~ t) .
(oneMinuteRate %~ M.clear) .
(fiveMinuteRate %~ M.clear) .
(fifteenMinuteRate %~ M.clear)
{-# INLINEABLE clear #-}
tick :: Meter -> Meter
tick = (oneMinuteRate %~ M.tick) . (fiveMinuteRate %~ M.tick) . (fifteenMinuteRate %~ M.tick)
{-# INLINEABLE tick #-}
tickIfNecessary :: NominalDiffTime -> Meter -> Meter
tickIfNecessary new d = if age >= meterTickInterval d
then iterate tick (d { meterLastTick = latest }) !! truncate (age / meterTickInterval d)
else d
where
age = realToFrac (new - meterLastTick d)
swapped = meterLastTick d < new
latest = Prelude.max (meterLastTick d) new
{-# INLINEABLE tickIfNecessary #-}
meanRate :: NominalDiffTime -> Meter -> Double
meanRate t d = if c == 0
then 0
else fromIntegral c / elapsed
where
c = meterCount d
start = meterStartTime d
elapsed = realToFrac (t - start)
{-# INLINEABLE meanRate #-}
oneMinuteAverage :: Meter -> M.MovingAverage
oneMinuteAverage = meterOneMinuteRate
fiveMinuteAverage :: Meter -> M.MovingAverage
fiveMinuteAverage = meterFiveMinuteRate
fifteenMinuteAverage :: Meter -> M.MovingAverage
fifteenMinuteAverage = meterFifteenMinuteRate