packages feed

freckle-app-1.0.1.0: library/Freckle/App/Datadog/Rts.hs

-- | RTS statistics sent to Datadog
module Freckle.App.Datadog.Rts
  ( forkRtsStatPolling
  ) where

import Prelude

import qualified Control.Immortal as Immortal
import Control.Monad (void)
import Control.Monad.IO.Unlift (MonadUnliftIO, liftIO)
import Control.Monad.Reader (MonadReader)
import Data.Foldable (traverse_)
import qualified Data.HashMap.Strict as HashMap
import Data.Text (Text)
import Freckle.App.Datadog (HasDogStatsClient, HasDogStatsTags)
import qualified Freckle.App.Datadog as Datadog
import qualified System.Metrics as Ekg
import qualified System.Metrics.Distribution.Internal as Ekg
import UnliftIO.Concurrent (threadDelay)

-- | Initialize a thread to poll RTS stats
--
-- Stats are collected via `ekg-core` and 'System.Metrics.registerGcMetrics'
--
forkRtsStatPolling
  :: ( MonadUnliftIO m
     , MonadReader env m
     , HasDogStatsClient env
     , HasDogStatsTags env
     )
  => m ()
forkRtsStatPolling = do
  store <- liftIO Ekg.newStore
  liftIO $ Ekg.registerGcMetrics store

  void $ Immortal.create $ \_ -> do
    sample <- liftIO $ Ekg.sampleAll store
    traverse_ (uncurry flushEkgSample) $ HashMap.toList sample

    let seconds n = n * 1000000
    threadDelay $ seconds 1

flushEkgSample
  :: ( MonadUnliftIO m
     , MonadReader env m
     , HasDogStatsClient env
     , HasDogStatsTags env
     )
  => Text
  -> Ekg.Value
  -> m ()
flushEkgSample name = \case
  Ekg.Counter n -> Datadog.counter name [] $ fromIntegral n
  Ekg.Gauge n -> Datadog.gauge name [] $ fromIntegral n
  Ekg.Distribution d -> do
    Datadog.gauge (name <> "." <> "mean") [] $ Ekg.mean d
    Datadog.gauge (name <> "." <> "variance") [] $ Ekg.variance d
    Datadog.gauge (name <> "." <> "sum") [] $ Ekg.sum d
    Datadog.gauge (name <> "." <> "min") [] $ Ekg.min d
    Datadog.gauge (name <> "." <> "max") [] $ Ekg.max d
    Datadog.counter (name <> "." <> "count") [] $ fromIntegral $ Ekg.count d
  Ekg.Label _ -> pure ()