{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Main where
import Control.Concurrent (threadDelay)
import Control.Monad (forM, forM_, forever)
import qualified Data.Text.Lazy as T
import qualified Data.Text.Encoding as TEnc
import qualified Data.Text.Format as TF
import LuminescentDreams.Logger
import Options.Applicative
import System.Statgrab
data Config = Config { interval :: Int
, watchCpu :: Bool
, watchLoad :: Bool
, watchMemory :: Bool
, watchFilesystems :: [String]
}
deriving (Show)
main :: IO ()
main = do
cli@Config{..} <- execParser (info (helper <*> cliParser)
(fullDesc <> progDesc "regularly stat the system"))
print cli
let logger = Logger (putStrLn . T.unpack) LogInfo
let modules = mconcat [ if watchCpu then [cpuModule logger] else []
, if watchLoad then [loadModule logger] else []
, if watchMemory then [memoryModule logger] else []
, [filesystemModule watchFilesystems logger]
]
forever $ do
forM modules $ \m -> m
threadDelay (interval * 1000000)
cliParser :: Parser Config
cliParser =
Config <$> option auto (long "interval" <> help "number of seconds between stats" <> value 5)
<*> switch (long "cpu" <> help "enable cpu monitoring")
<*> switch (long "load" <> help "enable system load monitoring")
<*> switch (long "mem" <> help "enable memory monitoring")
<*> many (strOption (long "fs" <> help "enable filesystem monitoring"))
cpuModule :: Logger -> IO ()
cpuModule logger =
runStats snapshot >>= \CPU{..} -> logMsg logger LogInfo [("stats", "cpu")] (show cpuTotal)
loadModule :: Logger -> IO ()
loadModule logger =
runStats snapshot >>= \Load{..} -> logMsg logger LogInfo [("stats", "load")] $ T.unpack $ TF.format "{}, {}, {}" (load1, load5, load15)
memoryModule :: Logger -> IO ()
memoryModule logger =
runStats snapshot >>=
\Memory{..} -> logMsg logger LogInfo [("stats", "memory")] $
T.unpack $ TF.format "{}, {}, {}, {}" ( bytesToMb memUsed
, bytesToMb memCache
, bytesToMb memFree
, bytesToMb memTotal
)
filesystemModule :: [String] -> Logger -> IO ()
filesystemModule fsNames logger = do
let fsNamesBs = map (TEnc.encodeUtf8 . T.toStrict . T.pack) fsNames
sts <- filter (\FileSystem{..} -> fsMountPoint `elem` fsNamesBs) <$> runStats snapshots
forM_ sts $ \FileSystem{..} ->
logMsg logger LogInfo [("stats", "filesystem")] $
T.unpack $ TF.format "{}: {}/{} [{}]" ( TEnc.decodeUtf8 fsMountPoint
, bytesToGb fsUsed
, bytesToGb fsSize
, bytesToGb fsAvail
)
bytesToMb :: Integer -> Float
bytesToMb s = fromIntegral s / 1024 / 1024
bytesToGb :: Integer -> Float
bytesToGb s = fromIntegral s / 1024 / 1024 / 1024