Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/parsonsmatt/monad-metrics

haskell metrics
https://github.com/parsonsmatt/monad-metrics

Last synced: 5 days ago
JSON representation

haskell metrics

Awesome Lists containing this project

README

        

# `monad-metrics`

[![Build Status](https://travis-ci.org/parsonsmatt/monad-metrics.svg?branch=master)](https://travis-ci.org/parsonsmatt/monad-metrics)

This library defines a convenient wrapper and API for using [EKG][] metrics in
your application. It's heavily inspired by the metrics code that Taylor Fausak
used in his Haskell application [blunt](https://github.com/tfausak/blunt).

# Usage

This [README is an executable literate Haskell
file](https://github.com/silky/literate-readme). If you have [stack][] installed, then you can run the file with:

```
./README.lhs
```

We'll need to start with the import/pragma boilerplate:

```haskell
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

import qualified Control.Monad.Metrics as Metrics
import Control.Monad.Metrics (Metrics, Resolution(..), MonadMetrics(..))
import Control.Monad.Reader
import qualified System.Metrics as EKG
```

The `Control.Monad.Metrics` module is designed to be imported qualified.

### Initialize!

First, you need to initialize the `Metrics` data type. You can do so using
`initialize` (to create a new EKG store) or `initializeWith` if you want to
pass a preexisting store.

```haskell
initializing :: Bool -> EKG.Store -> IO Metrics
initializing True store = Metrics.initializeWith store
initializing False _ = Metrics.initialize
```

### Embed!

The next step is to implement an instance of the class `MonadMetrics` for your
monad transformer stack. This library has explicitly decided not to provide a
concrete monad transformer to reduce the dependency footprint. Fortunately,
it's pretty easy!

Suppose you've got the following stack:

```haskell
type App = ReaderT Config IO

data Config = Config { configMetrics :: Metrics }
```

then you can easily get the required instance with:

```haskell
instance MonadMetrics (ReaderT Config IO) where
getMetrics = asks configMetrics
```

Now, you're off to the races! Let's record some metrics.

If you're after a really simple embedding, you can use `run` or `run'`:

```haskell
simple :: Int -> IO ()
simple i =
Metrics.run $ do
metrics <- Metrics.getMetrics
Metrics.gauge "Simple" i
forM_ [1..i] $ \_ -> do
Metrics.increment "Count!"

gettingThere :: IO ()
gettingThere =
Metrics.run' (\metrics -> Config metrics) $ do
liftIO $ putStrLn "it accepts a constructor"
```

### Measure!

Once your application has the required instance, you can use [EKG][]'s metrics
(counters, gauges, labels, distributions).

For detailed descriptions of the various metric types, see the corresponding [EKG][] documentation:

- [Counter][]
- [Distribution][]
- [Gauge][]
- [Label][]

Generally, the library provides "sane default" functions which accept the name
of the metric to work with and the value to contribute to that metric.

```haskell
w = Metrics.label "Foo" "Bar"
x = Metrics.counter "MetricName" 6
y = Metrics.distribution "Distribute" 3.4
z = Metrics.gauge "Gauge" 7
```

Generalized versions of these functions are available with an apostrophe. Labels accept any `Show`able value, while gauges and counters accept any `Integral` value.

```haskell
a = Metrics.label' "List" [1,2,3]
b = Metrics.counter' "Count" (3 :: Integer)
```

#### Timers

You can time actions with `timed`, which has a resolution of seconds. You can
use `timed'` which accepts a `Resolution` argument to provide a different
scale.

```haskell
timedProcess :: App Int
timedProcess =
Metrics.timed "summing1" $ do
pure $! sum [1 .. 100000]

timedInMilliseconds :: App Int
timedInMilliseconds =
Metrics.timed' Microseconds "summing2" $ do
pure $! sum [1..100]
```

# A demonstration

```haskell
main :: IO ()
main = do
metrics <- Metrics.initialize
flip runReaderT (Config metrics) $ do
Metrics.label "ProgramName" "README"
forM_ [1..10] $ \_ -> do
Metrics.increment "up-to-ten"
Metrics.timed' Nanoseconds "Whatever" $ do
liftIO $ putStrLn "Hello World!"
```

[EKG]: http://hackage.haskell.org/package/ekg-core
[stack]: https://www.haskellstack.org/
[Counter]: http://hackage.haskell.org/package/ekg-core-0.1.1.1/docs/System-Metrics-Counter.html
[Gauge]: http://hackage.haskell.org/package/ekg-core-0.1.1.1/docs/System-Metrics-Gauge.html
[Distribution]: http://hackage.haskell.org/package/ekg-core-0.1.1.1/docs/System-Metrics-Distribution.html
[Label]: http://hackage.haskell.org/package/ekg-core-0.1.1.1/docs/System-Metrics-Label.html