{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Prometheus.GHC (ghcStats) where import Control.Concurrent import Control.Monad (forever) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.State (MonadState) import Data.Monoid (mempty) import qualified GHC.Stats as GHC import Prometheus newUnlabelledMetric :: (MonadState Registry m, MonadIO m) => MetricName -> MetricHelp -> Metric a -> m a newUnlabelledMetric n h t = register n h mempty t {-| Register metrics that record internal GHC runtime metrics, and fork a new thread to periodically update those metrics. Returns the 'ThreadId' of the monitoring thread. The following metrics will be registered: * @ghc_bytes_allocated_total@: Total number of bytes allocated * @ghc_num_gcs_total@: Number of garbage collections performed (any generation, major and minor) * @ghc_max_bytes_used_total@: Maximum number of live bytes seen so far * @ghc_num_byte_usage_samples_total@: Number of byte usage samples taken, or equivalently the number of major GCs performed. * @ghc_cumulative_bytes_used_total@: Sum of all byte usage samples, can be used with numByteUsageSamples to calculate averages with arbitrary weighting (if you are sampling this record multiple times). * @ghc_bytes_copied_total@: Number of bytes copied during GC * @ghc_current_bytes_used@: Number of live bytes at the end of the last major GC * @ghc_current_bytes_slop@: Current number of bytes lost to slop * @ghc_max_bytes_slop_total@: Maximum number of bytes lost to slop at any one time so far * @ghc_peak_bytes_allocated_total@: Maximum number of bytes allocated * @ghc_mutator_cpu_seconds_total@: CPU time spent running mutator threads. This does not include any profiling overhead or initialization. * @ghc_mutator_wall_seconds_total@: Wall clock time spent running mutator threads. This does not include initialization. * @ghc_gc_cpu_seconds_total@: CPU time spent running GC * @ghc_gc_wall_seconds_total@: Wall clock time spent running GC * @ghc_cpu_seconds_total@: Total CPU time elapsed since program start * @ghc_wall_seconds_total@: Total wall clock time elapsed since start * @ghc_par_bytes_copied_total@: Number of bytes copied during GC, minus space held by mutable lists held by the capabilities. Can be used with parMaxBytesCopied to determine how well parallel GC utilized all cores. * @ghc_par_max_bytes_copied_total@: Sum of number of bytes copied each GC by the most active GC thread each GC. The ratio of parTotBytesCopied divided by parMaxBytesCopied approaches 1 for a maximally sequential run and approaches the number of threads (set by the RTS flag -N) for a maximally parallel run. * @ghc_prometheus_collection_time_seconds@: Amount of time spent by the Prometheus library collecting GHC statistics * @ghc_prometheus_record_time_seconds@: Amount of time spent by the Prometheus library recording GHC statistics -} ghcStats :: (MonadState Registry m, MonadIO m) => m (IO ThreadId) ghcStats = do bytesAllocated <- newUnlabelledMetric "ghc_bytes_allocated_total" "Total number of bytes allocated" gauge numGcs <- newUnlabelledMetric "ghc_num_gcs_total" "Number of garbage collections performed (any generation, major and minor)" gauge maxBytesUsed <- newUnlabelledMetric "ghc_max_bytes_used_total" "Maximum number of live bytes seen so far" gauge numByteUsageSamples <- newUnlabelledMetric "ghc_num_byte_usage_samples_total" "Number of byte usage samples taken, or equivalently the number of major GCs performed." gauge cumulativeBytesUsed <- newUnlabelledMetric "ghc_cumulative_bytes_used_total" "Sum of all byte usage samples, can be used with numByteUsageSamples to calculate averages with arbitrary weighting (if you are sampling this record multiple times)." gauge bytesCopied <- newUnlabelledMetric "ghc_bytes_copied_total" "Number of bytes copied during GC" gauge currentBytesUsed <- newUnlabelledMetric "ghc_current_bytes_used" "Number of live bytes at the end of the last major GC" gauge currentBytesSlop <- newUnlabelledMetric "ghc_current_bytes_slop" "Current number of bytes lost to slop" gauge maxBytesSlop <- newUnlabelledMetric "ghc_max_bytes_slop_total" "Maximum number of bytes lost to slop at any one time so far" gauge peakBytesAllocated <- newUnlabelledMetric "ghc_peak_bytes_allocated_total" "Maximum number of bytes allocated" gauge mutatorCpuSeconds <- newUnlabelledMetric "ghc_mutator_cpu_seconds_total" "CPU time spent running mutator threads. This does not include any profiling overhead or initialization." gauge mutatorWallSeconds <- newUnlabelledMetric "ghc_mutator_wall_seconds_total" "Wall clock time spent running mutator threads. This does not include initialization." gauge gcCpuSeconds <- newUnlabelledMetric "ghc_gc_cpu_seconds_total" "CPU time spent running GC" gauge gcWallSeconds <- newUnlabelledMetric "ghc_gc_wall_seconds_total" "Wall clock time spent running GC" gauge cpuSeconds <- newUnlabelledMetric "ghc_cpu_seconds_total" "Total CPU time elapsed since program start" gauge wallSeconds <- newUnlabelledMetric "ghc_wall_seconds_total" "Total wall clock time elapsed since start" gauge parTotBytesCopied <- newUnlabelledMetric "ghc_par_bytes_copied_total" "Number of bytes copied during GC, minus space held by mutable lists held by the capabilities. Can be used with parMaxBytesCopied to determine how well parallel GC utilized all cores." gauge parMaxBytesCopied <- newUnlabelledMetric "ghc_par_max_bytes_copied_total" "Sum of number of bytes copied each GC by the most active GC thread each GC. The ratio of parTotBytesCopied divided by parMaxBytesCopied approaches 1 for a maximally sequential run and approaches the number of threads (set by the RTS flag -N) for a maximally parallel run." gauge ghcStatsTime <- newUnlabelledMetric "ghc_prometheus_collection_time_seconds" "Amount of time spent by the Prometheus library collecting GHC statistics" (histogram (exponentialBuckets 1e-12 10 10)) ghcRecordTime <- newUnlabelledMetric "ghc_prometheus_record_time_seconds" "Amount of time spent by the Prometheus library recording GHC statistics" (histogram (exponentialBuckets 1e-12 10 10)) return $ liftIO $ forkIO $ do enabled <- GHC.getGCStatsEnabled case enabled of False -> putStrLn "GHC Statisics are not enabled." True -> forever $ do stats <- time ghcStatsTime GHC.getGCStats time ghcRecordTime $ do setGauge bytesAllocated (fromIntegral $ GHC.bytesAllocated stats) setGauge numGcs (fromIntegral $ GHC.numGcs stats) setGauge maxBytesUsed (fromIntegral $ GHC.maxBytesUsed stats) setGauge numByteUsageSamples (fromIntegral $ GHC.numByteUsageSamples stats) setGauge cumulativeBytesUsed (fromIntegral $ GHC.cumulativeBytesUsed stats) setGauge bytesCopied (fromIntegral $ GHC.bytesCopied stats) setGauge currentBytesUsed (fromIntegral $ GHC.currentBytesUsed stats) setGauge currentBytesSlop (fromIntegral $ GHC.currentBytesSlop stats) setGauge maxBytesSlop (fromIntegral $ GHC.maxBytesSlop stats) setGauge peakBytesAllocated (fromIntegral $ GHC.peakMegabytesAllocated stats * 1000000) setGauge mutatorCpuSeconds (GHC.mutatorCpuSeconds stats) setGauge mutatorWallSeconds (GHC.mutatorWallSeconds stats) setGauge gcCpuSeconds (GHC.gcCpuSeconds stats) setGauge gcWallSeconds (GHC.gcWallSeconds stats) setGauge cpuSeconds (GHC.cpuSeconds stats) setGauge wallSeconds (GHC.wallSeconds stats) setGauge parTotBytesCopied (fromIntegral $ GHC.parTotBytesCopied stats) setGauge parMaxBytesCopied (fromIntegral $ GHC.parMaxBytesCopied stats) threadDelay 1000000