module Network.Metric.Sink.Ganglia (
Slope(..)
, GangliaType(..)
, GangliaMetric(..)
, Ganglia(..)
, defaultMetric
, putMetaData
, putValue
, Sink(..)
, open
, Group
, Bucket
, Metric(..)
) where
import Data.Binary.Put
import Data.Bits ((.&.))
import Data.Char (toLower)
import Data.Data (Data)
import Data.Default (Default, def)
import Data.Int (Int32)
import Data.Maybe (fromMaybe)
import Data.Typeable (Typeable, typeOf)
import Data.Word (Word32)
import Network.Socket (SocketType(..))
import Network.Metric.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 :: Bucket
, type' :: GangliaType
, units :: BS.ByteString
, value :: BS.ByteString
, host :: BS.ByteString
, spoof :: BS.ByteString
, group :: Group
, slope :: Slope
, tmax :: Word32
, dmax :: Word32
} deriving (Show)
instance Default GangliaMetric where
def = defaultMetric
data Ganglia = Ganglia (Maybe Host) Handle deriving (Show)
instance Sink Ganglia where
push (Ganglia host hd) m = mapM_ (hPush hd) (concatMap enc $ measure m)
where
enc (Counter g b v) = put host g b v Positive
enc (Timer g b v) = put host g b v Both
enc (Gauge g b v) = put host g b v Both
close (Ganglia _ hd) = hClose hd
defaultMetric :: GangliaMetric
defaultMetric = GangliaMetric
{ name = ""
, type' = Int32
, units = ""
, value = ""
, host = ""
, spoof = ""
, group = ""
, slope = Both
, tmax = 60
, dmax = 0
}
open :: Maybe Host -> HostName -> PortNumber -> IO AnySink
open host = fOpen (Ganglia host) Datagram
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
put :: Encodable a
=> Maybe Host
-> Group
-> Bucket
-> a
-> Slope
-> [BL.ByteString]
put host group bucket value slope = map run [putMetaData, putValue]
where
run f = runPut $ f metric
metric = defaultMetric
{ name = bucket
, group = group
, host = fromMaybe "" host
, value = encode value
, type' = determineType value
, slope = slope
}
determineType :: Typeable a => a -> GangliaType
determineType value = case show $ typeOf value of
"Int16" -> Int16
"Int" -> Int32
"Integer" -> Int32
"Int32" -> Int32
"Float" -> Float
"Double" -> Double
_ -> String
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