Initial commit

master
Abhinav Sarkar 2017-11-15 18:00:34 +05:30
commit a08d3e6867
11 changed files with 675 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc*
/.purs*
/.psa*

19
bower.json Normal file
View File

@ -0,0 +1,19 @@
{
"name": "purescript-metrics",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"output"
],
"dependencies": {
"purescript-prelude": "^3.1.1",
"purescript-console": "^3.0.0",
"purescript-aff": "^3.1.0",
"purescript-maybe": "^3.0.0",
"purescript-foreign-generic": "^5.0.0"
},
"devDependencies": {
"purescript-psci-support": "^3.0.0"
}
}

213
src/System/Metrics.purs Normal file
View File

@ -0,0 +1,213 @@
module System.Metrics
( Store
, MetricSampler(..)
, Value(..)
, newStore
, register
, registerOrGet
, get
, registerCounter
, registerGauge
, registerHistogram
, registerMeter
, registerTimer
, createOrGetCounter
, createOrGetGuage
, createOrGetHistogramWithExponentialDecaySampling
, createOrGetHistogramWithUniformSampling
, createOrGetMeter
, createOrGetTimer
, sampleOne
, sample
) where
import Prelude
import Control.Monad.Aff (Aff)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Exception (EXCEPTION, throw)
import Control.Monad.Eff.Ref (REF, Ref, modifyRef', newRef, readRef)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Traversable (sequence)
import System.Metrics.Counter (Counter)
import System.Metrics.Counter as Counter
import System.Metrics.Gauge (Gauge)
import System.Metrics.Gauge as Gauge
import System.Metrics.Histogram (Histogram)
import System.Metrics.Histogram as Histogram
import System.Metrics.Meter (Meter)
import System.Metrics.Meter as Meter
import System.Metrics.Timer (Timer)
import System.Metrics.Timer as Timer
-- | A mutable metric store.
newtype Store = Store (Ref State)
-- | The 'Store' state.
type State = Map.Map String MetricSampler
data MetricSampler = CounterS Counter
| GaugeS Gauge
| HistogramS Histogram
| MeterS Meter
| TimerS Timer
-- | Create a new, empty metric store.
newStore ::forall eff. Eff (ref :: REF | eff) Store
newStore = do
ref <- newRef Map.empty
pure $ Store ref
register :: forall eff.
String -> MetricSampler -> Store -> Eff (ref :: REF | eff) Boolean
register name sampler (Store store) = modifyRef' store $ \state ->
case Map.lookup name state of
Just s -> { state : state, value : false }
Nothing -> { state : Map.insert name sampler state, value : true }
registerOrGet :: forall eff.
String -> MetricSampler -> Store -> Eff (ref :: REF | eff) MetricSampler
registerOrGet name sampler (Store store) = modifyRef' store $ \state ->
case Map.lookup name state of
Just s -> { state : state, value : s }
Nothing -> { state : Map.insert name sampler state, value : sampler }
get :: forall eff. String -> Store -> Eff (ref :: REF | eff) (Maybe MetricSampler)
get name (Store store) = readRef store >>= pure <<< Map.lookup name
-- | Register a non-negative, monotonically increasing, integer-valued metric.
-- |
-- | Also see 'createCounter'.
registerCounter :: forall eff.
String -> Counter -> Store -> Eff (ref :: REF | eff) Boolean
registerCounter name counter = register name (CounterS counter)
-- | Create and register a zero-initialized counter. Throw exception is the counter is already created.
createOrGetCounter :: forall eff.
String
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Counter
createOrGetCounter name store = do
counter <- Counter.new
registerOrGet name (CounterS counter) store >>= case _ of
CounterS c -> pure c
_ -> throw $ "Metric name is already registered: " <> name
-- | Register an integer-valued metric.
-- |
-- | Also see 'createGuage'.
registerGauge :: forall eff.
String -> Gauge -> Store -> Eff (ref :: REF | eff) Boolean
registerGauge name gauge = register name (GaugeS gauge)
-- | Create and register a guage. Throw exception is the guage is already created.
createOrGetGuage :: forall eff.
String
-> (forall e. Aff e Int)
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Gauge
createOrGetGuage name f store = do
let gauge = Gauge.new f
registerOrGet name (GaugeS gauge) store >>= case _ of
GaugeS g -> pure g
_ -> throw $ "Metric name is already registered: " <> name
registerHistogram :: forall eff.
String -> Histogram -> Store -> Eff (ref :: REF | eff) Boolean
registerHistogram name hist = register name (HistogramS hist)
createOrGetHistogramWithExponentialDecaySampling
:: forall eff.
String
-> Int
-> Number
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Histogram
createOrGetHistogramWithExponentialDecaySampling name size alpha store = do
hist <- Histogram.newWithExponentialDecaySampling size alpha
registerOrGet name (HistogramS hist) store >>= case _ of
HistogramS c -> pure c
_ -> throw $ "Metric name is already registered: " <> name
createOrGetHistogramWithUniformSampling
:: forall eff.
String
-> Int
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Histogram
createOrGetHistogramWithUniformSampling name size store = do
hist <- Histogram.newWithUniformSampling size
registerOrGet name (HistogramS hist) store >>= case _ of
HistogramS c -> pure c
_ -> throw $ "Metric name is already registered: " <> name
registerMeter :: forall eff. String -> Meter -> Store -> Eff (ref :: REF | eff) Boolean
registerMeter name meter = register name (MeterS meter)
createOrGetMeter
:: forall eff.
String
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Meter
createOrGetMeter name store = do
meter <- Meter.new
registerOrGet name (MeterS meter) store >>= case _ of
MeterS c -> pure c
_ -> throw $ "Metric name is already registered: " <> name
registerTimer :: forall eff. String -> Timer -> Store -> Eff (ref :: REF | eff) Boolean
registerTimer name timer = register name (TimerS timer)
createOrGetTimer
:: forall eff.
String
-> Store
-> Eff (ref :: REF, exception :: EXCEPTION | eff) Timer
createOrGetTimer name store = do
timer <- Timer.new
registerOrGet name (TimerS timer) store >>= case _ of
TimerS c -> pure c
_ -> throw $ "Metric name is already registered: " <> name
type Sample = Map.Map String Value
data Value = CounterV Int
| GaugeV Int
| HistogramV Histogram.Summary
| MeterV Meter.Summary
| TimerV Timer.Summary
derive instance eqVal :: Eq Value
derive instance genVal :: Generic Value _
instance showVal :: Show Value where
show = genericShow
sampleOne :: forall eff. MetricSampler -> Aff (ref :: REF | eff) Value
sampleOne (CounterS c) = CounterV <$> liftEff (Counter.read c)
sampleOne (GaugeS g) = GaugeV <$> Gauge.read g
sampleOne (HistogramS h) = HistogramV <$> liftEff (Histogram.read h)
sampleOne (MeterS h) = MeterV <$> liftEff (Meter.read h)
sampleOne (TimerS h) = TimerV <$> liftEff (Timer.read h)
sample :: forall eff. Store -> Aff (ref :: REF | eff) Sample
sample (Store store) = do
state <- liftEff $ readRef store
sequence $ map sampleOne state
-- main = do
-- store <- newStore
-- counter <- createOrGetCounter "testc" store
-- gauge <- createOrGetGuage "testg" (pure 3) store
-- hist <- createOrGetHistogramWithExponentialDecaySampling "hizz" 1028 0.015 store
-- meter <- createOrGetMeter "mmm" store
-- timer <- createOrGetTimer "ttt" store
-- Counter.inc counter 2
-- Histogram.update hist 1.2
-- Histogram.update hist 2.1
-- Meter.mark meter
-- Timer.update timer (Milliseconds 1000.0)
-- launchAff $ sample store >>= logShow

View File

@ -0,0 +1,28 @@
var Counter = require("metrics/metrics/counter");
var _new = function() {
return new Counter();
};
var read = function(c) {
return function() {
return c.count;
};
};
var _inc = function(c, v) {
return function() {
c.inc(v);
};
};
var reset = function(c) {
return function() {
c.value = 0;
}
}
exports._new = _new;
exports.read = read;
exports._inc = _inc;
exports.reset = reset;

View File

@ -0,0 +1,18 @@
module System.Metrics.Counter (Counter, new, read, inc) where
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Ref (REF)
import Data.Function.Uncurried (Fn2, runFn2)
import Prelude (Unit)
foreign import data Counter :: Type
foreign import _new :: forall eff. Eff (ref :: REF | eff) Counter
foreign import read :: forall eff. Counter -> Eff (ref :: REF | eff) Int
foreign import reset :: forall eff. Counter -> Eff (ref :: REF | eff) Unit
foreign import _inc :: forall eff. Fn2 Counter Int (Eff (ref :: REF | eff) Unit)
new :: forall eff. Eff (ref :: REF | eff) Counter
new = _new
inc :: forall eff. Counter -> Int -> Eff (ref :: REF | eff) Unit
inc c = runFn2 _inc c

View File

@ -0,0 +1,11 @@
module System.Metrics.Gauge (Gauge, new, read) where
import Control.Monad.Aff (Aff)
newtype Gauge = Gauge (forall eff. Aff eff Int)
new :: (forall eff. Aff eff Int) -> Gauge
new = Gauge
read :: forall eff. Gauge -> Aff eff Int
read (Gauge f) = f

View File

@ -0,0 +1,60 @@
var Histogram = require("metrics/metrics/histogram");
var clear = function(h) {
return function() {
h.clear();
};
};
var _update = function(h, v) {
return function() {
h.update(v);
};
};
var _percentiles = function(h, ptiles) {
return function() {
var scoresM = h.percentiles(ptiles);
var scores = [];
for (var i = 0; i < ptiles.length; i++) {
scores.push(scoresM[ptiles[i]]);
}
if (scores[0]) {
return scores;
}
return null;
};
};
var _variance = function(h) { return function() { return h.variance(); }; };
var _mean = function(h) { return function() { return h.mean(); }; };
var _stdDev = function(h) { return function() { return h.stdDev(); }; };
var _min = function(h) { return function() { return h.min; }; };
var _max = function(h) { return function() { return h.max; }; };
var _sum = function(h) { return function() { return h.sum; }; };
var count = function(h) { return function() { return h.count; }; };
var _newWithExponentialDecaySampling = function(size, alpha) {
return function() {
return Histogram.createExponentialDecayHistogram(size, alpha);
};
};
var newWithUniformSampling = function(size) {
return function() {
return Histogram.createUniformHistogram(size);
};
};
exports.clear = clear;
exports._update = _update;
exports._percentiles = _percentiles;
exports._variance = _variance;
exports._mean = _mean;
exports._stdDev = _stdDev;
exports._min = _min;
exports._max = _max;
exports._sum = _sum;
exports.count = count;
exports._newWithExponentialDecaySampling = _newWithExponentialDecaySampling;
exports.newWithUniformSampling = newWithUniformSampling;

View File

@ -0,0 +1,114 @@
module System.Metrics.Histogram
( Histogram
, Summary
, newWithExponentialDecaySampling
, newWithUniformSampling
, clear
, update
, percentiles
, variance
, mean
, stdDev
, min
, max
, sum
, count
, read
) where
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Ref (REF)
import Data.Array (index)
import Data.Function.Uncurried (Fn2, runFn2)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Maybe (Maybe)
import Data.Nullable (Nullable, toMaybe)
import Prelude
foreign import data Histogram :: Type
foreign import _newWithExponentialDecaySampling ::
forall eff. Fn2 Int Number (Eff (ref :: REF | eff) Histogram)
foreign import newWithUniformSampling ::
forall eff. Int -> Eff (ref :: REF | eff) Histogram
foreign import clear :: forall eff. Histogram -> Eff (ref :: REF | eff) Unit
foreign import _update :: forall eff. Fn2 Histogram Number (Eff (ref :: REF | eff) Unit)
foreign import _percentiles
:: forall eff. Fn2 Histogram (Array Number) (Eff (ref :: REF | eff) (Nullable (Array Number)))
foreign import _variance :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import _mean :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import _stdDev :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import _min :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import _max :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import _sum :: forall eff. Histogram -> Eff (ref :: REF | eff) (Nullable Number)
foreign import count :: forall eff. Histogram -> Eff (ref :: REF | eff) Int
newWithExponentialDecaySampling :: forall eff. Int -> Number -> Eff (ref :: REF | eff) Histogram
newWithExponentialDecaySampling = runFn2 _newWithExponentialDecaySampling
update :: forall eff. Histogram -> Number -> Eff (ref :: REF | eff) Unit
update = runFn2 _update
percentiles :: forall eff. Histogram -> Array Number -> Eff (ref :: REF | eff) (Maybe (Array Number))
percentiles h ptiles = toMaybe <$> runFn2 _percentiles h ptiles
variance :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
variance h = toMaybe <$> _variance h
mean :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
mean h = toMaybe <$> _mean h
stdDev :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
stdDev h = toMaybe <$> _stdDev h
min :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
min h = toMaybe <$> _min h
max :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
max h = toMaybe <$> _max h
sum :: forall eff. Histogram -> Eff (ref :: REF | eff) (Maybe Number)
sum h = toMaybe <$> _sum h
newtype Summary = Summary {
min :: Maybe Number
, max :: Maybe Number
, sum :: Maybe Number
, variance :: Maybe Number
, mean :: Maybe Number
, stdDev :: Maybe Number
, count :: Int
, median :: Maybe Number
, p75 :: Maybe Number
, p95 :: Maybe Number
, p99 :: Maybe Number
, p999 :: Maybe Number
}
derive instance eqSummary :: Eq Summary
derive instance genericSummary :: Generic Summary _
instance showSummary :: Show Summary where
show = genericShow
read :: forall eff. Histogram -> Eff (ref :: REF | eff) Summary
read h = do
ptiles <- percentiles h [0.5, 0.75, 0.95, 0.99, 0.999]
Summary <$> ({ min: _
, max: _
, sum: _
, variance: _
, mean: _
, stdDev: _
, count: _
, median: ptiles >>= flip index 0
, p75: ptiles >>= flip index 1
, p95: ptiles >>= flip index 2
, p99: ptiles >>= flip index 3
, p999: ptiles >>= flip index 4
} <$> min h
<*> max h
<*> sum h
<*> variance h
<*> mean h
<*> stdDev h
<*> count h)

View File

@ -0,0 +1,49 @@
var Meter = require("metrics/metrics/meter");
var _new = function() {
return new Meter();
};
var _markN = function(m, n) {
return function() {
m.mark(n);
};
};
var fifteenMinuteRate = function(m) {
return function() {
return m.fifteenMinuteRate();
};
};
var fiveMinuteRate = function(m) {
return function() {
return m.fiveMinuteRate();
};
};
var oneMinuteRate = function(m) {
return function() {
return m.oneMinuteRate();
};
};
var meanRate = function(m) {
return function() {
return m.meanRate();
};
};
var count = function(m) {
return function() {
return m.count;
};
};
exports._new = _new;
exports._markN = _markN;
exports.fifteenMinuteRate = fifteenMinuteRate;
exports.fiveMinuteRate = fiveMinuteRate;
exports.oneMinuteRate = oneMinuteRate;
exports.meanRate = meanRate;
exports.count = count;

View File

@ -0,0 +1,63 @@
module System.Metrics.Meter
( Meter
, Summary
, new
, markN
, mark
, fifteenMinuteRate
, fiveMinuteRate
, oneMinuteRate
, meanRate
, count
, read
) where
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Ref (REF)
import Data.Function.Uncurried (Fn2, runFn2)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Prelude (class Eq, class Show, Unit, (<$>), (<*>))
foreign import data Meter :: Type
foreign import _new :: forall eff. Eff (ref :: REF | eff) Meter
foreign import _markN :: forall eff. Fn2 Meter Int (Eff (ref :: REF | eff) Unit)
foreign import fifteenMinuteRate :: forall eff. Meter -> Eff (ref :: REF | eff) Number
foreign import fiveMinuteRate :: forall eff. Meter -> Eff (ref :: REF | eff) Number
foreign import oneMinuteRate :: forall eff. Meter -> Eff (ref :: REF | eff) Number
foreign import meanRate :: forall eff. Meter -> Eff (ref :: REF | eff) Number
foreign import count :: forall eff. Meter -> Eff (ref :: REF | eff) Int
new :: forall eff. Eff (ref :: REF | eff) Meter
new = _new
markN :: forall eff. Meter -> Int -> Eff (ref :: REF | eff) Unit
markN = runFn2 _markN
mark :: forall eff. Meter -> Eff (ref :: REF | eff) Unit
mark m = markN m 1
newtype Summary = Summary {
count :: Int
, m1 :: Number
, m5 :: Number
, m15 :: Number
, mean :: Number
}
derive instance eqSummary :: Eq Summary
derive instance genericSummary :: Generic Summary _
instance showSummary :: Show Summary where
show = genericShow
read :: forall eff. Meter -> Eff (ref :: REF | eff) Summary
read m = Summary <$> ({ count: _
, m1: _
, m5: _
, m15: _
, mean: _
} <$> count m
<*> oneMinuteRate m
<*> fiveMinuteRate m
<*> fifteenMinuteRate m
<*> meanRate m)

View File

@ -0,0 +1,92 @@
module System.Metrics.Timer
( Timer
, Summary
, new
, update
, fifteenMinuteRate
, fiveMinuteRate
, oneMinuteRate
, meanRate
, clear
, percentiles
, mean
, stdDev
, min
, max
, sum
, count
, read
) where
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Ref (REF)
import Data.Generic.Rep (class Generic)
import Data.Generic.Rep.Show (genericShow)
import Data.Maybe (Maybe)
import Data.Time.Duration (class Duration, Milliseconds(..), fromDuration)
import System.Metrics.Histogram (Histogram)
import System.Metrics.Histogram as Histogram
import System.Metrics.Meter (Meter)
import System.Metrics.Meter as Meter
newtype Timer = Timer { meter :: Meter, histogram :: Histogram }
new :: forall eff. Eff (ref :: REF | eff) Timer
new = Timer <$> ({ meter: _, histogram: _ }
<$> Meter.new
<*> Histogram.newWithExponentialDecaySampling 1028 0.015)
update :: forall a eff. Duration a => Timer -> a -> Eff (ref :: REF | eff) Unit
update (Timer { meter, histogram }) d = do
let (Milliseconds ms) = fromDuration d
Histogram.update histogram ms
Meter.mark meter
fifteenMinuteRate :: forall eff. Timer -> Eff (ref :: REF | eff) Number
fifteenMinuteRate (Timer { meter }) = Meter.fifteenMinuteRate meter
fiveMinuteRate :: forall eff. Timer -> Eff (ref :: REF | eff) Number
fiveMinuteRate (Timer { meter }) = Meter.fiveMinuteRate meter
oneMinuteRate :: forall eff. Timer -> Eff (ref :: REF | eff) Number
oneMinuteRate (Timer { meter }) = Meter.oneMinuteRate meter
meanRate :: forall eff. Timer -> Eff (ref :: REF | eff) Number
meanRate (Timer { meter }) = Meter.meanRate meter
clear :: forall eff. Timer -> Eff (ref :: REF | eff) Unit
clear (Timer { histogram }) = Histogram.clear histogram
percentiles :: forall eff. Timer -> Array Number -> Eff (ref :: REF | eff) (Maybe (Array Number))
percentiles (Timer { histogram }) = Histogram.percentiles histogram
mean :: forall eff. Timer -> Eff (ref :: REF | eff) (Maybe Number)
mean (Timer { histogram }) = Histogram.mean histogram
stdDev :: forall eff. Timer -> Eff (ref :: REF | eff) (Maybe Number)
stdDev (Timer { histogram }) = Histogram.stdDev histogram
min :: forall eff. Timer -> Eff (ref :: REF | eff) (Maybe Number)
min (Timer { histogram }) = Histogram.min histogram
max :: forall eff. Timer -> Eff (ref :: REF | eff) (Maybe Number)
max (Timer { histogram }) = Histogram.max histogram
sum :: forall eff. Timer -> Eff (ref :: REF | eff) (Maybe Number)
sum (Timer { histogram }) = Histogram.sum histogram
count :: forall eff. Timer -> Eff (ref :: REF | eff) Int
count (Timer { histogram }) = Histogram.count histogram
newtype Summary = Summary { duration :: Histogram.Summary, rate :: Meter.Summary }
derive instance eqSummary :: Eq Summary
derive instance genericSummary :: Generic Summary _
instance showSummary :: Show Summary where
show = genericShow
read :: forall eff. Timer -> Eff (ref :: REF | eff) Summary
read (Timer { meter, histogram }) =
Summary <$> ({ duration: _, rate: _} <$> Histogram.read histogram <*> Meter.read meter)