-
Notifications
You must be signed in to change notification settings - Fork 38
Dimensional counters #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
module System.Metrics.Dimensional where | ||
|
||
import qualified System.Metrics.Counter as Counter | ||
import qualified System.Metrics.Distribution as Distribution | ||
import qualified System.Metrics.Gauge as Gauge | ||
import qualified System.Metrics.Label as Label | ||
|
||
import Prelude hiding (lookup) | ||
import GHC.Exception (Exception) | ||
import Control.Exception (throwIO) | ||
import Data.Text (Text) | ||
import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef) | ||
import Data.HashMap.Strict (HashMap, empty) | ||
import qualified Data.HashMap.Strict as HashMap | ||
|
||
type Name = Text | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lots of Haddoc docs needed here. |
||
type Explanation = Text | ||
type Dimensions = [Text] | ||
type Point = [Text] | ||
|
||
data Dimensional a = Dimensional { | ||
_create :: !(IO a) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please stick to the field naming pattern used for other data types (i.e. no leading underscore, but prefixed as needed). |
||
, _name :: !Name | ||
, _explanation :: !Explanation | ||
, _dimensions :: !Dimensions | ||
, _points :: !(IORef (HashMap Point a)) | ||
} | ||
|
||
newDimensional :: IO a -> Name -> Explanation -> Dimensions -> IO (Dimensional a) | ||
newDimensional new name explanation dimensions = | ||
Dimensional new name explanation dimensions <$> newIORef empty | ||
|
||
data DimensionError | ||
= DimensionError !Name !Dimensions !Point | ||
deriving (Show, Ord, Eq) | ||
instance Exception DimensionError | ||
|
||
data LookupFailure | ||
= UnmatchedDimensions DimensionError | ||
| NotFound | ||
deriving (Show, Ord, Eq) | ||
|
||
lookup :: Dimensional a -> Point -> IO (Either LookupFailure a) | ||
lookup d pt | ||
| not $ matchDimensions (_dimensions d) pt = pure $ Left (UnmatchedDimensions err) | ||
| otherwise = do | ||
toLookupResult . HashMap.lookup pt <$> readIORef (_points d) | ||
where | ||
err :: DimensionError | ||
err = DimensionError (_name d) (_dimensions d) pt | ||
|
||
toLookupResult Nothing = Left NotFound | ||
toLookupResult (Just x) = Right x | ||
|
||
matchDimensions :: Dimensions -> Point -> Bool | ||
matchDimensions ds ps = length ds == length ps | ||
|
||
create :: Dimensional a -> Point -> IO a | ||
create d pt | ||
| not $ matchDimensions (_dimensions d) pt = throwIO err | ||
| otherwise = do | ||
v <- _create d | ||
atomicModifyIORef' (_points d) (\store -> (HashMap.insert pt v store, ())) | ||
return v | ||
where | ||
err :: DimensionError | ||
err = DimensionError (_name d) (_dimensions d) pt | ||
|
||
lookupOrCreate :: Dimensional a -> Point -> IO a | ||
lookupOrCreate d pt = lookup d pt >>= \case | ||
Left NotFound -> create d pt | ||
Left (UnmatchedDimensions err) -> throwIO err | ||
Right x -> return x | ||
|
||
type Counter = Dimensional Counter.Counter | ||
type Gauge = Dimensional Gauge.Gauge | ||
type Label = Dimensional Label.Label | ||
type Distribution = Dimensional Distribution.Distribution | ||
|
||
example :: IO () | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This belongs in the examples/ directory and/or in some docs. |
||
example = do | ||
c <- newDimensional Counter.new "foo" "a foo" ["url", "status"] | ||
let url = "/hello" | ||
let status = "200" | ||
x <- lookupOrCreate c [url, status] | ||
Counter.inc x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file needs some top-level Haddocks explaining how to use this module and what it's for.