module System.Metrics.Prometheus.Ridley.Metrics.DiskUsage (
getDiskStats
, mkDiskGauge
, diskUsageMetrics
) where
import Control.Monad
import Control.Monad.IO.Class
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Text as T
import Lens.Micro
import Lens.Micro.TH
import qualified System.Metrics.Prometheus.Metric.Gauge as P
import qualified System.Metrics.Prometheus.MetricId as P
import qualified System.Metrics.Prometheus.RegistryT as P
import System.Metrics.Prometheus.Ridley.Types
import System.Process
import Text.Read
data DiskStats = DiskStats {
_diskFilesystem :: T.Text
, _diskUsed :: Double
, _diskFree :: Double
} deriving Show
makeLenses ''DiskStats
data DiskMetric = DiskMetric {
_dskMetricUsed :: P.Gauge
, _dskMetricFree :: P.Gauge
}
type DiskUsageMetrics = M.Map T.Text DiskMetric
getDiskStats :: IO [DiskStats]
getDiskStats = do
let diskOnly = (\d -> "/dev" `T.isInfixOf` (d ^. diskFilesystem))
let dropHeader = drop 1
rawLines <- dropHeader . T.lines . T.strip . T.pack <$> readProcess "df" [] []
return $ filter diskOnly . mapMaybe mkDiskStats $ rawLines
where
mkDiskStats :: T.Text -> Maybe DiskStats
mkDiskStats rawLine = case T.words rawLine of
#ifdef darwin_HOST_OS
[fs,_, used,free,_,_,_,_,_] -> DiskStats <$> pure fs
<*> readMaybe (T.unpack used)
<*> readMaybe (T.unpack free)
#else
[fs,_, used,free,_,_] -> DiskStats <$> pure fs
<*> readMaybe (T.unpack used)
<*> readMaybe (T.unpack free)
#endif
_ -> Nothing
updateDiskUsageMetric :: DiskMetric -> DiskStats -> Bool -> IO ()
updateDiskUsageMetric DiskMetric{..} d _ = do
P.set (d ^. diskUsed) _dskMetricUsed
P.set (d ^. diskFree) _dskMetricFree
updateDiskUsageMetrics :: DiskUsageMetrics -> Bool -> IO ()
updateDiskUsageMetrics dmetrics flush = do
diskStats <- getDiskStats
forM_ diskStats $ \d -> do
let key = d ^. diskFilesystem
case M.lookup key dmetrics of
Nothing -> return ()
Just m -> updateDiskUsageMetric m d flush
diskUsageMetrics :: DiskUsageMetrics -> RidleyMetricHandler
diskUsageMetrics g = RidleyMetricHandler {
metric = g
, updateMetric = updateDiskUsageMetrics
, flush = False
}
mkDiskGauge :: MonadIO m => P.Labels -> DiskUsageMetrics -> DiskStats -> P.RegistryT m DiskUsageMetrics
mkDiskGauge currentLabels dmap d = do
let fs = d ^. diskFilesystem
let finalLabels = P.addLabel "filesystem" fs currentLabels
metric <- DiskMetric <$> P.registerGauge "disk_used_bytes_blocks" finalLabels
<*> P.registerGauge "disk_free_bytes_blocks" finalLabels
liftIO $ updateDiskUsageMetric metric d False
return $! M.insert fs metric $! dmap