packages feed

monad-metrics 0.1.0.0 → 0.1.0.1

raw patch · 4 files changed

+189/−39 lines, 4 filesdep +microlensPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: microlens

API changes (from Hackage documentation)

- Control.Monad.Metrics.Internal: [metricsCounters] :: Metrics -> IORef (Map Text Counter)
- Control.Monad.Metrics.Internal: [metricsDistributions] :: Metrics -> IORef (Map Text Distribution)
- Control.Monad.Metrics.Internal: [metricsGauges] :: Metrics -> IORef (Map Text Gauge)
- Control.Monad.Metrics.Internal: [metricsLabels] :: Metrics -> IORef (Map Text Label)
- Control.Monad.Metrics.Internal: [metricsStore] :: Metrics -> Store
+ Control.Monad.Metrics: instance (Control.Monad.Metrics.MonadMetrics m, Control.Monad.Trans.Class.MonadTrans t, GHC.Base.Monad (t m)) => Control.Monad.Metrics.MonadMetrics (t m)
+ Control.Monad.Metrics: instance GHC.Base.Monad m => Control.Monad.Metrics.MonadMetrics (Control.Monad.Trans.Reader.ReaderT Control.Monad.Metrics.Internal.Metrics m)
+ Control.Monad.Metrics: metricsCounters :: Lens' Metrics (IORef (Map Text Counter))
+ Control.Monad.Metrics: metricsGauges :: Lens' Metrics (IORef (Map Text Gauge))
+ Control.Monad.Metrics: metricsLabels :: Lens' Metrics (IORef (Map Text Label))
+ Control.Monad.Metrics: metricsStore :: Lens' Metrics Store
+ Control.Monad.Metrics: run :: MonadIO m => ReaderT Metrics m a -> m a
+ Control.Monad.Metrics: run' :: MonadIO m => (Metrics -> r) -> ReaderT r m a -> m a
+ Control.Monad.Metrics.Internal: [_metricsCounters] :: Metrics -> IORef (Map Text Counter)
+ Control.Monad.Metrics.Internal: [_metricsDistributions] :: Metrics -> IORef (Map Text Distribution)
+ Control.Monad.Metrics.Internal: [_metricsGauges] :: Metrics -> IORef (Map Text Gauge)
+ Control.Monad.Metrics.Internal: [_metricsLabels] :: Metrics -> IORef (Map Text Label)
+ Control.Monad.Metrics.Internal: [_metricsStore] :: Metrics -> Store
+ Control.Monad.Metrics.Internal: metricsCounters :: Lens' Metrics (IORef (Map Text Counter))
+ Control.Monad.Metrics.Internal: metricsDistributions :: Lens' Metrics (IORef (Map Text Distribution))
+ Control.Monad.Metrics.Internal: metricsGauges :: Lens' Metrics (IORef (Map Text Gauge))
+ Control.Monad.Metrics.Internal: metricsLabels :: Lens' Metrics (IORef (Map Text Label))
+ Control.Monad.Metrics.Internal: metricsStore :: Lens' Metrics Store
- Control.Monad.Metrics: class MonadMetrics m
+ Control.Monad.Metrics: class Monad m => MonadMetrics m

Files

README.md view
@@ -26,7 +26,7 @@ {-# LANGUAGE NoMonomorphismRestriction #-}  import qualified Control.Monad.Metrics as Metrics-import           Control.Monad.Metrics (Metrics, Resolution(..), MonadMetrics)+import           Control.Monad.Metrics (Metrics, Resolution(..), MonadMetrics(..)) import           Control.Monad.Reader import qualified System.Metrics        as EKG ```@@ -68,6 +68,23 @@ ```  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! 
monad-metrics.cabal view
@@ -1,7 +1,7 @@ name:                monad-metrics-version:             0.1.0.0+version:             0.1.0.1 synopsis:            A convenient wrapper around EKG metrics-description:         Please see README.md+description:         A convenient wrapper for collecting application metrics. Please see the README.md for more information. homepage:            https://github.com/parsonsmatt/monad-metrics#readme license:             MIT license-file:        LICENSE@@ -24,6 +24,7 @@                      , text                       < 1.3                      , mtl          >= 2       && < 2.3                      , transformers >= 0.3     && < 0.6+                     , microlens    >= 0.2     && < 0.5   default-language:    Haskell2010  test-suite monad-metrics-test
src/Control/Monad/Metrics.hs view
@@ -1,6 +1,12 @@+{-# LANGUAGE DefaultSignatures          #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE FunctionalDependencies     #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RecordWildCards            #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE UndecidableInstances       #-}  {-| Module      : Control.Monad.Metrics@@ -12,8 +18,10 @@ Stability   : experimental Portability : POSIX -This module presents an easy interface that you can use to collect metrics about your application.-It uses EKG under the hood and is inspired by Taylor Fausak's blunt application.+This module presents an easy interface that you can use to collect metrics+about your application.  It uses EKG from "System.Metrics" under the hood+and is inspired by Taylor Fausak's <https://github.com/tfausak/blunt blunt>+application.  This module is designed to be imported qualified. -}@@ -21,10 +29,13 @@     ( -- * The Type Class       MonadMetrics(..)       -- * Initializing-    , Metrics+      -- $initializing     , initialize     , initializeWith+    , run+    , run'       -- * Collecting Metrics+      -- $collecting     , increment     , counter     , counter'@@ -36,16 +47,26 @@     , label     , label'     , Resolution(..)+    -- * The Metrics Type+    -- $metrictype+    , Metrics+    , metricsCounters+    , metricsGauges+    , metricsLabels+    , metricsStore     ) where -import Control.Monad (liftM)-import Data.Monoid (mempty)-import           Control.Monad.IO.Class+import           Control.Monad                  (liftM)+import           Control.Monad.IO.Class         (MonadIO (..))+import           Control.Monad.Reader           (MonadReader (..), ReaderT (..))+import           Control.Monad.Trans            (MonadTrans (..)) import           Data.IORef import           Data.Map                       (Map) import qualified Data.Map                       as Map+import           Data.Monoid                    (mempty) import           Data.Text                      (Text) import qualified Data.Text                      as Text+import           Lens.Micro import           System.Clock                   (Clock (..), TimeSpec (..),                                                  getTime) import qualified System.Metrics                 as EKG@@ -54,73 +75,133 @@ import           System.Metrics.Gauge           as Gauge import           System.Metrics.Label           as Label -import Prelude+import           Prelude  import           Control.Monad.Metrics.Internal --- | A class is an instance of 'MonadMetrics' if it can provide a 'Metrics'+-- | A type can be an instance of 'MonadMetrics' if it can provide a 'Metrics' -- somehow. Commonly, this will be implemented as a 'ReaderT' where some -- field in the environment is the 'Metrics' data. ----- Since v0.1.0.0-class MonadMetrics m where+-- * /Since v0.1.0.0/+class Monad m => MonadMetrics m where     getMetrics :: m Metrics +instance {-# OVERLAPPABLE #-} (MonadMetrics m, MonadTrans t, Monad (t m)) => MonadMetrics (t m) where+    getMetrics = lift getMetrics++instance Monad m => MonadMetrics (ReaderT Metrics m) where+    getMetrics = ask++-- $initializing+-- This library tends to provide simple functions with plain names and+-- generalized functions with apostrophes. When initializing the metrics,+-- you can use 'initialize' if you don't need fine control over the store,+-- or you can use 'initializeWith' if your application already has a store+-- that it uses.+--+-- Likewise, we provide 'run' for the simplest case, and 'run'' for the+-- more complex case where you have some larger type.+--+-- The most flexible way to use the library is to implement the+-- 'MonadMetrics' class.++-- | Enhances the base monad with metrics. This works for very simple+-- cases, where you don't have a 'Reader' involved yet. If your stack+-- already has a 'Reader', then you'll get some annoying type problems with+-- this. Switch over to 'run'', or alternatively, define your own+-- 'MonadMetrics' instance.+--+-- */Since v0.1.0.0/+run :: MonadIO m => ReaderT Metrics m a -> m a+run = run' id++-- | Adds metric recording capabilities to the given action. The first+-- parameter is a function which accepts a 'Metrics' value and creates the+-- final @r@ value to be used in the action. This is useful when you have+-- a preexisting 'ReaderT' in your stack, and you want to enhance it with+-- metrics.+--+-- @+-- data Config = Config { size :: Int, metrics' :: Metrics }+--+-- main = 'runWithMetrics' (Config 10) $ do+--     num <- asks size+--     forM_ [1 .. size] \_ -> Metrics.increment "foo"+-- @+--+-- */Since v0.1.0.0/+run' :: MonadIO m => (Metrics -> r) -> ReaderT r m a -> m a+run' k action = do+    m <- liftIO initialize+    runReaderT action (k m)+ -- | Initializes a 'Metrics' value with the given 'System.Metrics.Store'. ----- Since 0.1.0.0+-- */Since v0.1.0.0/ initializeWith :: EKG.Store -> IO Metrics-initializeWith metricsStore = do-    metricsCounters <- newIORef mempty-    metricsDistributions <- newIORef mempty-    metricsGauges <- newIORef mempty-    metricsLabels <- newIORef mempty+initializeWith _metricsStore = do+    _metricsCounters <- newIORef mempty+    _metricsDistributions <- newIORef mempty+    _metricsGauges <- newIORef mempty+    _metricsLabels <- newIORef mempty     return Metrics{..}  -- | Initializes a 'Metrics' value, creating a new 'System.Metrics.Store' -- for it. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ initialize :: IO Metrics initialize = EKG.newStore >>= initializeWith +-- $collecting+-- As with initialization, the library provides "common case" functions+-- with a plain name and generalized functions with an apostrophe.+--+-- * 'increment', 'counter', 'counter''+-- * 'gauge', 'gauge''+-- * 'timed', 'timed''+-- * 'label', 'label''+--+-- Only 'distribution' isn't generalized.+ -- | Increment the named counter by 1. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ increment :: (MonadIO m, MonadMetrics m) => Text -> m () increment name = counter name 1  -- | Adds the value to the named 'System.Metrics.Counter.Counter'. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ counter' :: (MonadIO m, MonadMetrics m, Integral int) => Text -> int -> m () counter' =-    modifyMetric Counter.add fromIntegral EKG.createCounter metricsCounters+    modifyMetric Counter.add fromIntegral EKG.createCounter _metricsCounters  -- | A type specialized version of 'counter'' to avoid ambiguous type -- errors. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ counter :: (MonadIO m, MonadMetrics m) => Text -> Int -> m () counter = counter'  -- | Add the value to the named 'System.Metrics.Distribution.Distribution'. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ distribution :: (MonadIO m, MonadMetrics m) => Text -> Double -> m () distribution =-    modifyMetric Distribution.add id EKG.createDistribution metricsDistributions+    modifyMetric Distribution.add id EKG.createDistribution _metricsDistributions  -- | Set the value of the named 'System.Metrics.Distribution.Gauge'. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ gauge' :: (MonadIO m, MonadMetrics m, Integral int) => Text -> int -> m () gauge' =-    modifyMetric Gauge.set fromIntegral EKG.createGauge metricsGauges+    modifyMetric Gauge.set fromIntegral EKG.createGauge _metricsGauges  -- | A type specialized version of 'gauge'' to avoid ambiguous types. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ gauge :: (MonadIO m, MonadMetrics m) => Text -> Int -> m () gauge = gauge' @@ -128,7 +209,7 @@ -- stored in a 'System.Metrics.Disribution.Distribution' and is converted -- to the specified 'Resolution'. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ timed' :: (MonadIO m, MonadMetrics m) => Resolution -> Text -> m a -> m a timed' resolution name action = do     start <- liftIO $ getTime Monotonic@@ -140,16 +221,28 @@ -- | Record the time of executing the given action in seconds. Defers to -- 'timed''. ----- Since v0.1.0.0+-- * /Since v0.1.0.0/ timed :: (MonadIO m, MonadMetrics m) => Text -> m a -> m a timed = timed' Seconds +-- | Set the 'Label' to the given 'Text' value.+--+-- * /Since v0.1.0.0/ label :: (MonadIO m, MonadMetrics m) => Text -> Text -> m ()-label = modifyMetric Label.set id EKG.createLabel metricsLabels+label = modifyMetric Label.set id EKG.createLabel _metricsLabels +-- | Set the 'Label' to the 'Show'n value of whatever you pass in.+--+-- * /Since v0.1.0.0/ label' :: (MonadIO m, MonadMetrics m, Show a) => Text -> a -> m () label' l = label l . Text.pack . show +-- $metrictype+-- The 'Metric' type contains an 'IORef' to a 'Map' from 'Text' labels to+-- the various counters, and a 'EKG.Store' to register them with. If you+-- must use the 'Metric' value directly, then you are recommended to use+-- the lenses provided for compatibility.+ -------------------------------------------------------------------------------  diffTime :: Resolution -> TimeSpec -> TimeSpec -> Double@@ -204,7 +297,7 @@     container <- liftIO $ readIORef ref     case Map.lookup name container of         Nothing -> do-            c <- liftIO . creator name =<< liftM metricsStore getMetrics+            c <- liftIO . creator name =<< liftM _metricsStore getMetrics             liftIO $ modifyIORef ref (Map.insert name c)             return c         Just c -> return c
src/Control/Monad/Metrics/Internal.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE RankNTypes #-}+ {-| Module      : Control.Monad.Metrics.Internal Description : An easy interface to recording metrics.@@ -8,14 +10,17 @@ Stability   : experimental Portability : POSIX -This is an internal module for+This is an internal module. Depend upon it at your own risk -- breaking+changes in here will /not/ be reflected in the major API version.  -} module Control.Monad.Metrics.Internal where +import           Control.Monad.Reader        (asks) import           Data.IORef import           Data.Map                    (Map) import           Data.Text                   (Text)+import           Lens.Micro import           System.Metrics              (Store) import           System.Metrics.Counter      (Counter) import           System.Metrics.Distribution (Distribution)@@ -23,16 +28,50 @@ import           System.Metrics.Label        (Label)  -- | A container for metrics used by the 'MonadMetrics' class.+--+-- * /Since v0.1.0.0/ data Metrics = Metrics-    { metricsCounters      :: IORef (Map Text Counter)-    , metricsGauges        :: IORef (Map Text Gauge)-    , metricsDistributions :: IORef (Map Text Distribution)-    , metricsLabels        :: IORef (Map Text Label)-    , metricsStore         :: Store+    { _metricsCounters      :: IORef (Map Text Counter)+    , _metricsGauges        :: IORef (Map Text Gauge)+    , _metricsDistributions :: IORef (Map Text Distribution)+    , _metricsLabels        :: IORef (Map Text Label)+    , _metricsStore         :: Store     } +-- | A lens into the 'Counter's provided by the 'Metrics'.+--+-- * /Since v0.1.0.0/+metricsCounters :: Lens' Metrics (IORef (Map Text Counter))+metricsCounters f (Metrics c g d l s) = fmap (\c' -> Metrics c' g d l s) (f c)++-- | A lens into the 'Gauge's provided by the 'Metrics'.+--+-- * /Since v0.1.0.0/+metricsGauges :: Lens' Metrics (IORef (Map Text Gauge))+metricsGauges f (Metrics c g d l s) = fmap (\g' -> Metrics c g' d l s) (f g)++-- | A lens into the 'Distribution's provided by the 'Metrics'.+--+-- * /Since v0.1.0.0/+metricsDistributions :: Lens' Metrics (IORef (Map Text Distribution))+metricsDistributions f (Metrics c g d l s) = fmap (\d' -> Metrics c g d' l s) (f d)++-- | A lens into the 'Label's provided by the 'Metrics'.+--+-- * /Since v0.1.0.0/+metricsLabels :: Lens' Metrics (IORef (Map Text Label))+metricsLabels f (Metrics c g d l s) = fmap (\l' -> Metrics c g d l' s) (f l)++-- | A lens into the 'Store' provided by the 'Metrics'.+--+-- * /Since v0.1.0.0/+metricsStore :: Lens' Metrics Store+metricsStore f (Metrics c g d l s) = fmap (Metrics c g d l) (f s)+ -- | A type representing the resolution of time to use for the 'timed' -- metric.+--+-- * /Since v0.1.0.0/ data Resolution     = Nanoseconds     | Microseconds