packages feed

systemstats-0.1.0.0: app/Main.hs

{-# 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