network-metrics 0.1.3 → 0.1.4
raw patch · 7 files changed
+185/−183 lines, 7 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Network.Metrics: Handle :: Socket -> SockAddr -> Handle
- Network.Metrics: data Handle
- Network.Metrics.Ganglia: data Handle
- Network.Metrics.Ganglia: emit :: Metric -> Handle -> IO ()
- Network.Metrics.Ganglia: instance Data MetricType
- Network.Metrics.Ganglia: instance Default Metric
- Network.Metrics.Ganglia: instance Eq MetricType
- Network.Metrics.Ganglia: instance Show Metric
- Network.Metrics.Ganglia: instance Show MetricType
- Network.Metrics.Ganglia: instance Typeable MetricType
- Network.Metrics.Ganglia: test :: String -> String -> IO a
- Network.Metrics.Graphite: bucket :: Metric -> ByteString
- Network.Metrics.Graphite: data Handle
- Network.Metrics.Graphite: emit :: Metric -> Handle -> IO ()
- Network.Metrics.Graphite: instance Show Metric
- Network.Metrics.Graphite: value :: Metric -> ByteString
- Network.Metrics.Statsd: bucket :: Metric -> ByteString
- Network.Metrics.Statsd: data Handle
- Network.Metrics.Statsd: emit :: Metric -> Handle -> IO ()
- Network.Metrics.Statsd: instance Show Metric
- Network.Metrics.Statsd: instance Show MetricType
- Network.Metrics.Statsd: rate :: Metric -> Double
- Network.Metrics.Statsd: type' :: Metric -> MetricType
- Network.Metrics.Statsd: value :: Metric -> ByteString
+ Network.Metrics.Ganglia: Counter :: MetricType
+ Network.Metrics.Ganglia: Ganglia :: Handle -> Ganglia
+ Network.Metrics.Ganglia: GangliaMetric :: ByteString -> GangliaType -> ByteString -> ByteString -> ByteString -> ByteString -> ByteString -> Slope -> Word32 -> Word32 -> GangliaMetric
+ Network.Metrics.Ganglia: Gauge :: MetricType
+ Network.Metrics.Ganglia: Timer :: MetricType
+ Network.Metrics.Ganglia: class MetricSink a
+ Network.Metrics.Ganglia: data Ganglia
+ Network.Metrics.Ganglia: data GangliaMetric
+ Network.Metrics.Ganglia: data GangliaType
+ Network.Metrics.Ganglia: instance Data GangliaType
+ Network.Metrics.Ganglia: instance Default GangliaMetric
+ Network.Metrics.Ganglia: instance Eq GangliaType
+ Network.Metrics.Ganglia: instance MetricSink Ganglia
+ Network.Metrics.Ganglia: instance Show GangliaMetric
+ Network.Metrics.Ganglia: instance Show GangliaType
+ Network.Metrics.Ganglia: instance Typeable GangliaType
+ Network.Metrics.Ganglia: push :: MetricSink a => Metric -> a -> IO ()
+ Network.Metrics.Ganglia: putMetaData :: GangliaMetric -> Put
+ Network.Metrics.Ganglia: putValue :: GangliaMetric -> Put
+ Network.Metrics.Ganglia: type Bucket = ByteString
+ Network.Metrics.Ganglia: type Group = ByteString
+ Network.Metrics.Ganglia: type Value = ByteString
+ Network.Metrics.Graphite: Counter :: MetricType
+ Network.Metrics.Graphite: Gauge :: MetricType
+ Network.Metrics.Graphite: Timer :: MetricType
+ Network.Metrics.Graphite: class MetricSink a
+ Network.Metrics.Graphite: data MetricType
+ Network.Metrics.Graphite: instance MetricSink Graphite
+ Network.Metrics.Graphite: push :: MetricSink a => Metric -> a -> IO ()
+ Network.Metrics.Graphite: type Bucket = ByteString
+ Network.Metrics.Graphite: type Group = ByteString
+ Network.Metrics.Graphite: type Value = ByteString
+ Network.Metrics.Statsd: class MetricSink a
+ Network.Metrics.Statsd: instance MetricSink Statsd
+ Network.Metrics.Statsd: instance Show StatsdMetric
+ Network.Metrics.Statsd: push :: MetricSink a => Metric -> a -> IO ()
+ Network.Metrics.Statsd: type Bucket = ByteString
+ Network.Metrics.Statsd: type Group = ByteString
+ Network.Metrics.Statsd: type Value = ByteString
- Network.Metrics.Ganglia: Double :: MetricType
+ Network.Metrics.Ganglia: Double :: GangliaType
- Network.Metrics.Ganglia: Float :: MetricType
+ Network.Metrics.Ganglia: Float :: GangliaType
- Network.Metrics.Ganglia: Int16 :: MetricType
+ Network.Metrics.Ganglia: Int16 :: GangliaType
- Network.Metrics.Ganglia: Int32 :: MetricType
+ Network.Metrics.Ganglia: Int32 :: GangliaType
- Network.Metrics.Ganglia: Int8 :: MetricType
+ Network.Metrics.Ganglia: Int8 :: GangliaType
- Network.Metrics.Ganglia: Metric :: ByteString -> MetricType -> ByteString -> ByteString -> ByteString -> ByteString -> ByteString -> Slope -> Word32 -> Word32 -> Metric
+ Network.Metrics.Ganglia: Metric :: MetricType -> Group -> Bucket -> Value -> Metric
- Network.Metrics.Ganglia: String :: MetricType
+ Network.Metrics.Ganglia: String :: GangliaType
- Network.Metrics.Ganglia: UInt16 :: MetricType
+ Network.Metrics.Ganglia: UInt16 :: GangliaType
- Network.Metrics.Ganglia: UInt32 :: MetricType
+ Network.Metrics.Ganglia: UInt32 :: GangliaType
- Network.Metrics.Ganglia: UInt8 :: MetricType
+ Network.Metrics.Ganglia: UInt8 :: GangliaType
- Network.Metrics.Ganglia: close :: Handle -> IO ()
+ Network.Metrics.Ganglia: close :: MetricSink a => a -> IO ()
- Network.Metrics.Ganglia: defaultMetric :: Metric
+ Network.Metrics.Ganglia: defaultMetric :: GangliaMetric
- Network.Metrics.Ganglia: dmax :: Metric -> Word32
+ Network.Metrics.Ganglia: dmax :: GangliaMetric -> Word32
- Network.Metrics.Ganglia: group :: Metric -> ByteString
+ Network.Metrics.Ganglia: group :: GangliaMetric -> ByteString
- Network.Metrics.Ganglia: host :: Metric -> ByteString
+ Network.Metrics.Ganglia: host :: GangliaMetric -> ByteString
- Network.Metrics.Ganglia: name :: Metric -> ByteString
+ Network.Metrics.Ganglia: name :: GangliaMetric -> ByteString
- Network.Metrics.Ganglia: open :: String -> String -> IO Handle
+ Network.Metrics.Ganglia: open :: String -> String -> IO Ganglia
- Network.Metrics.Ganglia: slope :: Metric -> Slope
+ Network.Metrics.Ganglia: slope :: GangliaMetric -> Slope
- Network.Metrics.Ganglia: spoof :: Metric -> ByteString
+ Network.Metrics.Ganglia: spoof :: GangliaMetric -> ByteString
- Network.Metrics.Ganglia: tmax :: Metric -> Word32
+ Network.Metrics.Ganglia: tmax :: GangliaMetric -> Word32
- Network.Metrics.Ganglia: type' :: Metric -> MetricType
+ Network.Metrics.Ganglia: type' :: GangliaMetric -> GangliaType
- Network.Metrics.Ganglia: units :: Metric -> ByteString
+ Network.Metrics.Ganglia: units :: GangliaMetric -> ByteString
- Network.Metrics.Ganglia: value :: Metric -> ByteString
+ Network.Metrics.Ganglia: value :: GangliaMetric -> ByteString
- Network.Metrics.Graphite: Metric :: ByteString -> ByteString -> Metric
+ Network.Metrics.Graphite: Metric :: MetricType -> Group -> Bucket -> Value -> Metric
- Network.Metrics.Graphite: close :: Handle -> IO ()
+ Network.Metrics.Graphite: close :: MetricSink a => a -> IO ()
- Network.Metrics.Graphite: open :: String -> String -> IO Handle
+ Network.Metrics.Graphite: open :: String -> String -> IO Graphite
- Network.Metrics.Statsd: Metric :: MetricType -> ByteString -> ByteString -> Double -> Metric
+ Network.Metrics.Statsd: Metric :: MetricType -> Group -> Bucket -> Value -> Metric
- Network.Metrics.Statsd: close :: Handle -> IO ()
+ Network.Metrics.Statsd: close :: MetricSink a => a -> IO ()
- Network.Metrics.Statsd: open :: String -> String -> IO Handle
+ Network.Metrics.Statsd: open :: String -> String -> IO Statsd
Files
- network-metrics.cabal +2/−3
- src/GMetric.hs +13/−9
- src/Network/Metrics.hs +0/−18
- src/Network/Metrics/Ganglia.hs +71/−93
- src/Network/Metrics/Graphite.hs +28/−21
- src/Network/Metrics/Internal.hs +38/−11
- src/Network/Metrics/Statsd.hs +33/−28
network-metrics.cabal view
@@ -1,5 +1,5 @@ name: network-metrics-version: 0.1.3+version: 0.1.4 synopsis: Send metrics to Ganglia, Graphite, and statsd. license: OtherLicense license-file: LICENSE@@ -32,8 +32,7 @@ location: git://github.com/brendanhay/network-metrics.git library- exposed-modules: Network.Metrics- , Network.Metrics.Ganglia+ exposed-modules: Network.Metrics.Ganglia , Network.Metrics.Graphite , Network.Metrics.Statsd other-modules: Network.Metrics.Internal
src/GMetric.hs view
@@ -14,11 +14,13 @@ main ) where -import Control.Monad (when)-import Data.ByteString.Char8 (pack)+import Control.Monad (when)+import Data.Binary.Put (runPut)+import Data.ByteString.Char8 (pack) import System.Console.CmdArgs-import System.Environment (getArgs, withArgs)-import System.Exit (ExitCode(..), exitWith)+import System.Environment (getArgs, withArgs)+import System.Exit (ExitCode(..), exitWith)+import Network.Metrics.Internal import qualified Network.Metrics.Ganglia as G @@ -27,7 +29,7 @@ , optPort :: String , optName :: String , optValue :: String- , optType :: G.MetricType+ , optType :: G.GangliaType , optGroup :: String , optUnits :: String , optSlope :: G.Slope@@ -49,11 +51,13 @@ emit :: Options -> IO () emit Options{..} = do- handle <- G.open optHost optPort- G.emit metric handle- G.close handle+ sink@(G.Ganglia hd) <- G.open optHost optPort+ _ <- push' G.putMetaData hd+ _ <- push' G.putValue hd+ G.close sink where- metric = G.Metric+ push' f = hPush . runPut $ f metric+ metric = G.GangliaMetric (pack optName) optType (pack optUnits)
− src/Network/Metrics.hs
@@ -1,18 +0,0 @@--- |--- Module : Network.Metrics--- Copyright : (c) 2012 Brendan Hay <brendan@soundcloud.com>--- License : This Source Code Form is subject to the terms of--- the Mozilla Public License, v. 2.0.--- A copy of the MPL can be found in the LICENSE file or--- you can obtain it at http://mozilla.org/MPL/2.0/.--- Maintainer : Brendan Hay <brendan@soundcloud.com>--- Stability : experimental--- Portability : non-portable (GHC extensions)-----module Network.Metrics (- -- * Re-exported types- Handle(..)- ) where--import Network.Metrics.Internal
src/Network/Metrics/Ganglia.hs view
@@ -13,53 +13,61 @@ module Network.Metrics.Ganglia ( -- * Exported types Slope(..)- , MetricType(..)- , Metric(..)+ , GangliaType(..)+ , GangliaMetric(..)+ , Ganglia(..) -- * Default constructors , defaultMetric -- * Socket Handle operations , open- , emit - -- * Development helpers- , test+ -- * Binary encoding+ , putMetaData+ , putValue - -- * Re-exported from internal- , I.Handle- , I.close+ -- -- * Network.Metrics.Internal re-exported types+ , Group+ , Bucket+ , Value+ , MetricType(..)+ , Metric(..)+ , MetricSink(push)++ -- * Network.Metrics.Internal operations+ , close ) where -import Control.Concurrent (threadDelay)-import Control.Monad (unless, liftM)+import Control.Monad (liftM) import Data.Binary.Put-import Data.Bits ((.&.))-import Data.Char (toLower)-import Data.Data (Data, Typeable)-import Data.Default (Default, def)-import Data.Int (Int32)-import Data.Word (Word32)-import Network.Socket-import System.Random (randomRIO)+import Data.Bits ((.&.))+import Data.Char (toLower)+import Data.Data (Data, Typeable)+import Data.Default (Default, def)+import Data.Int (Int32)+import Data.Word (Word32)+import Network.Socket (SocketType(..))+import Network.Metrics.Internal -import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BS-import qualified Network.Metrics.Internal as I+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL + -- | Allows gmetad and the PHP webfrontend to efficiently separate -- constant data metrics from volatile ones data Slope = Zero | Positive | Negative | Both | Unspecified deriving (Data, Typeable, Show, Eq, Enum) -- | Metric types supported by Ganglia-data MetricType = String | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Float | Double+data GangliaType = String | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Float | Double deriving (Data, Typeable, Eq, Show) --- | Concrete metric type used to emit metadata and value packets-data Metric = Metric+-- | concrete metric type used to emit metadata and value packets+data GangliaMetric = GangliaMetric { name :: BS.ByteString- , type' :: MetricType+ , type' :: GangliaType , units :: BS.ByteString , value :: BS.ByteString , host :: BS.ByteString@@ -70,20 +78,26 @@ , dmax :: Word32 } deriving (Show) -instance Default Metric where+instance Default GangliaMetric where def = defaultMetric +data Ganglia = Ganglia Handle++instance MetricSink Ganglia where+ push m (Ganglia h) = hPush (encode m) h+ close (Ganglia h) = hClose h+ -- -- API -- -- | A default metric record-defaultMetric :: Metric-defaultMetric = Metric- { name = "magical_metric"- , type' = UInt16+defaultMetric :: GangliaMetric+defaultMetric = GangliaMetric+ { name = ""+ , type' = Int32 , units = ""- , value = "0"+ , value = "" , host = "" , spoof = "" , group = ""@@ -92,41 +106,17 @@ , dmax = 0 } --- | Create a new unconnected socket handle for UDP communication-open :: String -> String -> IO I.Handle-open = I.open Datagram---- | Emit a metric's metadata and value on the specified socket handle-emit :: Metric -> I.Handle -> IO ()-emit metric handle@(I.Handle sock addr) = do- sIsConnected sock >>= \b -> unless b $ connect sock addr- _ <- push putMetaData handle- _ <- push putMetric handle- return ()- where- push fn = I.emit . runPut $ fn metric------- Private------- TODO: enforce max buffer size length checks.--- Magic number is per libgmond.c-bufferSize :: Integer-bufferSize = 1500------- Binary Encoding---+open :: String -> String -> IO Ganglia+open host port = liftM Ganglia (hOpen Datagram host port) -- | Metric metadata -- -- The format for this can be found in either: -- * gm_protocol.x in the Ganglia 3.1 sources -- * https://github.com/lookfirst/jmxtrans-putMetaData :: Metric -> Put-putMetaData metric@Metric{..} = do- putHeader 128 metric -- 128 = metadata_msg+putMetaData :: GangliaMetric -> Put+putMetaData m@GangliaMetric{..} = do+ putHeader 128 m -- 128 = metadata_msg putType type' putString name putString units@@ -136,15 +126,30 @@ putGroup group -- | Metric value-putMetric :: Metric -> Put-putMetric metric@Metric{..} = do- putHeader 133 metric -- 133 = string_msg+putValue :: GangliaMetric -> Put+putValue m@GangliaMetric{..} = do+ putHeader 133 m -- 133 = string_msg putString "%s" putString value +--+-- Private+--++-- TODO: enforce max buffer size length checks.+-- Magic number is per libgmond.c+bufferSize :: Integer+bufferSize = 1500++encode :: Metric -> BL.ByteString+encode (Metric _ g b v) = BL.concat $ map put [putMetaData, putValue]+ where+ metric = defaultMetric { name = b, group = g, value = v }+ put f = runPut $ f metric+ -- | Common headers for the metadata and value-putHeader :: Int32 -> Metric -> Put-putHeader code Metric{..} = do+putHeader :: Int32 -> GangliaMetric -> Put+putHeader code GangliaMetric{..} = do putInt code putString host putString name@@ -178,32 +183,5 @@ where len = BS.length bstr -putType :: MetricType -> Put+putType :: GangliaType -> Put putType = putString . BS.pack . map toLower . show------- Development------- | Exactly one second!-oneSecond :: Int-oneSecond = 1000000---- | The min, max variance of test metrics-variance :: (Int, Int)-variance = (0, 1000)---- | Sample a random element from a list-sample :: [a] -> IO a-sample xs = liftM (xs !!) (randomRIO (0, length xs - 1))---- | Used to emit a default metric via GHCi-test :: String -> String -> IO a-test host port = open host port >>= loop- where- loop h = do- r <- randomRIO variance :: IO Int- n <- sample ["magic", "candy", "unicorns"]- emit defaultMetric { name = n, value = BS.pack $ show r } h- threadDelay oneSecond- loop h
src/Network/Metrics/Graphite.hs view
@@ -11,43 +11,50 @@ -- module Network.Metrics.Graphite (- -- * Exported types- Metric(..)- -- * Socket Handle operations- , open- , emit+ open - -- * Re-exported from internal- , I.Handle- , I.close+ -- * Network.Metrics.Internal re-exported types+ , Group+ , Bucket+ , Value+ , MetricType(..)+ , Metric(..)+ , MetricSink(push)++ -- * Network.Metrics.Internal operations+ , close ) where +import Control.Monad (liftM) import Network.Socket import Data.Time.Clock.POSIX+import Network.Metrics.Internal import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Network.Metrics.Internal as I -data Metric = Metric- { bucket :: BS.ByteString- , value :: BS.ByteString- } deriving (Show)+data Graphite = Graphite Handle +instance MetricSink Graphite where+ push m (Graphite h) = encode m >>= flip hPush h+ close (Graphite h) = hClose h -- -- API -- -- | Create a new disconnected socket handle for TCP communication-open :: String -> String -> IO I.Handle-open = I.open Stream+open :: String -> String -> IO Graphite+open host port = liftM Graphite (hOpen Stream host port) --- | Emit a metric's metadata and value on the specified socket handle-emit :: Metric -> I.Handle -> IO ()-emit Metric{..} handle = do- time <- getPOSIXTime- I.emit (encoded time) handle+--+-- Private+--++encode :: Metric -> IO BL.ByteString+encode (Metric _ g b v) = liftM bstr getPOSIXTime where- encoded n = BL.fromChunks [bucket, value, timestamp n]+ bucket = BS.concat [g, ".", b] timestamp n = BS.pack $ show (truncate n :: Integer)+ bstr n = BL.fromChunks [bucket, v, timestamp n]+
src/Network/Metrics/Internal.hs view
@@ -13,41 +13,68 @@ module Network.Metrics.Internal ( -- * Exported types Handle(..)+ , Group+ , Bucket+ , Value+ , MetricType(..)+ , Metric(..)+ , MetricSink(..) -- * Socket Handle operations- , open- , close- , emit+ , hOpen+ , hClose+ , hPush ) where import Control.Monad (unless) import Network.Socket hiding (send) import Network.Socket.ByteString.Lazy (send) +import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL -- | Socket handle data Handle = Handle Socket SockAddr deriving (Show) +-- | Metric group+type Group = BS.ByteString++-- | Metric bucket+type Bucket = BS.ByteString++-- | Metric value+type Value = BS.ByteString++-- | Metric type+data MetricType = Counter | Gauge | Timer deriving (Show)++-- | Concrete metric data type+data Metric = Metric MetricType Group Bucket Value deriving (Show)++-- | Describes a sink resource which is held open for metric emission+class MetricSink a where+ push :: Metric -> a -> IO ()+ close :: a -> IO ()+ -- -- API -- -- | Create a new unconnected socket handle for UDP communication-open :: SocketType -> String -> String -> IO Handle-open typ host port = do+hOpen :: SocketType -> String -> String -> IO Handle+hOpen typ host port = do (addr:_) <- getAddrInfo Nothing (Just host) (Just port) sock <- socket (addrFamily addr) typ defaultProtocol return $ Handle sock (addrAddress addr) -- | Close a socket handle-close :: Handle -> IO ()-close (Handle sock _) = sClose sock+hClose :: Handle -> IO ()+hClose (Handle sock _) = sClose sock --- | Push an encoded metric to the specified socket handle-emit :: BL.ByteString -> Handle -> IO ()-emit bstr (Handle sock addr) | BL.null bstr = return ()- | otherwise = do+-- | Direct access for writing a bytestring to the socket handle+hPush :: BL.ByteString -> Handle -> IO ()+hPush bstr (Handle sock addr) | BL.null bstr = return ()+ | otherwise = do sIsConnected sock >>= \b -> unless b $ connect sock addr _ <- send sock bstr return ()
src/Network/Metrics/Statsd.hs view
@@ -11,29 +11,30 @@ -- module Network.Metrics.Statsd (- -- * Exported types- MetricType(..)- , Metric(..)- -- * Socket Handle operations- , open- , emit+ open - -- * Re-exported from internal- , I.Handle- , I.close+ -- * Network.Metrics.Internal re-exported types+ , Group+ , Bucket+ , Value+ , MetricType(..)+ , Metric(..)+ , MetricSink(push)++ -- * Network.Metrics.Internal operations+ , close ) where -import Network.Socket-import System.Random (randomRIO)+import Control.Monad (liftM)+import Network.Socket (SocketType(..))+import System.Random (randomRIO)+import Network.Metrics.Internal import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Network.Metrics.Internal as I -data MetricType = Counter | Timer | Gauge deriving (Show)--data Metric = Metric+data StatsdMetric = StatsdMetric { type' :: MetricType , bucket :: BS.ByteString , value :: BS.ByteString@@ -42,33 +43,37 @@ data Sampled = Sampled | Exact | Ignore +data Statsd = Statsd Handle++instance MetricSink Statsd where+ push m (Statsd h) = encode m >>= flip hPush h+ close (Statsd h) = hClose h+ -- -- API -- -- | Create a new disconnected socket handle for UDP communication-open :: String -> String -> IO I.Handle-open = I.open Datagram---- | Emit a metric's metadata and value on the specified socket handle-emit :: Metric -> I.Handle -> IO ()-emit metric handle = do- rand <- randomRIO (0.0, 1.0)- I.emit (encoded rand) handle- where- encoded = BL.fromChunks . components metric . sample (rate metric)+open :: String -> String -> IO Statsd+open host port = liftM Statsd (hOpen Datagram host port) ----- Sampling+-- Private -- +encode :: Metric -> IO BL.ByteString+encode (Metric t g b v) = liftM bstr (randomRIO (0.0, 1.0))+ where+ metric = StatsdMetric t (BS.concat [g, ".", b]) v 1.0+ bstr = BL.fromChunks . chunks metric . sample (rate metric)+ sample :: Double -> Double -> Sampled sample rate rand | rate < 1.0 && rand <= rate = Sampled | rate == 1.0 = Exact | otherwise = Ignore -components :: Metric -> Sampled -> [BS.ByteString]-components Metric{..} sampled = case sampled of+chunks :: StatsdMetric -> Sampled -> [BS.ByteString]+chunks StatsdMetric{..} sampled = case sampled of Sampled -> base ++ ["@", BS.pack $ show rate] Exact -> base Ignore -> []