module Network.Metrics.Ganglia (
Slope(..)
, GangliaType(..)
, GangliaMetric(..)
, Ganglia(..)
, defaultMetric
, putMetaData
, putValue
, open
, MetricSink(push, close)
, Group
, Bucket
, Value
, MetricType(..)
, Metric(..)
) where
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 (SocketType(..))
import Network.Metrics.Internal
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BL
data Slope = Zero | Positive | Negative | Both | Unspecified
deriving (Data, Typeable, Show, Eq, Enum)
data GangliaType = String | Int8 | UInt8 | Int16 | UInt16 | Int32 | UInt32 | Float | Double
deriving (Data, Typeable, Eq, Show)
data GangliaMetric = GangliaMetric
{ name :: BS.ByteString
, type' :: GangliaType
, units :: BS.ByteString
, value :: BS.ByteString
, host :: BS.ByteString
, spoof :: BS.ByteString
, group :: BS.ByteString
, slope :: Slope
, tmax :: Word32
, dmax :: Word32
} deriving (Show)
instance Default GangliaMetric where
def = defaultMetric
data Ganglia = Ganglia Handle deriving (Show)
instance MetricSink Ganglia where
push m (Ganglia h) = hPush (encode m) h
close (Ganglia h) = hClose h
defaultMetric :: GangliaMetric
defaultMetric = GangliaMetric
{ name = ""
, type' = Int32
, units = ""
, value = ""
, host = ""
, spoof = ""
, group = ""
, slope = Both
, tmax = 60
, dmax = 0
}
open :: String -> String -> IO Ganglia
open host port = liftM Ganglia (hOpen Datagram host port)
putMetaData :: GangliaMetric -> Put
putMetaData m@GangliaMetric{..} = do
putHeader 128 m
putType type'
putString name
putString units
putEnum slope
putUInt tmax
putUInt dmax
putGroup group
putValue :: GangliaMetric -> Put
putValue m@GangliaMetric{..} = do
putHeader 133 m
putString "%s"
putString value
bufferSize :: Integer
bufferSize = 1500
encode :: Metric -> BL.ByteString
encode (Metric t g b v) = BL.concat $ map put [putMetaData, putValue]
where
slope' = case t of
Counter -> Positive
_ -> Both
metric = defaultMetric { name = b, group = g, value = v, slope = slope' }
put f = runPut $ f metric
putHeader :: Int32 -> GangliaMetric -> Put
putHeader code GangliaMetric{..} = do
putInt code
putString host
putString name
putString spoof
putGroup :: BS.ByteString -> Put
putGroup group | BS.null group = putInt 0
| otherwise = do
putInt 1
putString "GROUP"
putString group
putInt :: Int32 -> Put
putInt = putWord32be . fromIntegral
putUInt :: Word32 -> Put
putUInt = putWord32be
putEnum :: Enum a => a -> Put
putEnum = putInt . fromIntegral . fromEnum
putString :: BS.ByteString -> Put
putString bstr = do
putInt $ fromIntegral len
putByteString bstr
case fromIntegral len .&. 3 of
0 -> return ()
m -> putByteString $ B.replicate (4 m) 0
where
len = BS.length bstr
putType :: GangliaType -> Put
putType = putString . BS.pack . map toLower . show