content: {-# LANGUAGE ExistentialQuantification #-}
  module Prometheus.Registry (
      register
  ,   registerIO
  ,   unsafeRegister
  ,   unsafeRegisterIO
  ,   collectMetrics
  ,   unregisterAll
  ) where
  
  import Prometheus.Metric
  
  import Control.Applicative ((<$>))
  import Control.Monad.IO.Class
  import System.IO.Unsafe (unsafePerformIO)
  import qualified Control.Concurrent.STM as STM
  
  
  -- $setup
  -- >>> :module +Prometheus
  -- >>> unregisterAll
  
  -- | A 'Registry' is a list of all registered metrics, currently represented by
  -- their sampling functions.
  type Registry = [IO [SampleGroup]]
  
  {-# NOINLINE globalRegistry #-}
  globalRegistry :: STM.TVar Registry
  globalRegistry = unsafePerformIO $ STM.newTVarIO []
  
  -- | Registers a metric with the global metric registry.
  register :: MonadIO m => Metric s -> m s
  register (Metric mk) = liftIO $ do
      (metric, sampleGroups) <- mk
      let addToRegistry = (sampleGroups :)
      liftIO $ STM.atomically $ STM.modifyTVar' globalRegistry addToRegistry
      return metric
  
  -- | Registers a metric with the global metric registry.
  registerIO :: MonadIO m => m (Metric s) -> m s
  registerIO metricGen = metricGen >>= register
  
  -- | Registers a metric with the global metric registry.
  --
  -- __IMPORTANT__: This method should only be used to register metrics as top
  -- level symbols, it should not be run from other pure code.
  unsafeRegister :: Metric s -> s
  unsafeRegister = unsafePerformIO . register
  
  -- | Registers a metric with the global metric registry.
  --
  -- __IMPORTANT__: This method should only be used to register metrics as top
  -- level symbols, it should not be run from other pure code.
  --
  -- For example,
  --
  -- >>> :{
  --  {-# NOINLINE c #-}
  --  let c = unsafeRegisterIO $ counter (Info "my_counter" "An example metric")
  -- :}
  -- ...
  unsafeRegisterIO :: IO (Metric s) -> s
  unsafeRegisterIO = unsafePerformIO . registerIO
  
  -- | Removes all currently registered metrics from the registry.
  unregisterAll :: MonadIO m => m ()
  unregisterAll = liftIO $ STM.atomically $ STM.writeTVar globalRegistry []
  
  -- | Collect samples from all currently registered metrics. In typical use cases
  -- there is no reason to use this function, instead you should use
  -- `exportMetricsAsText` or a convenience library.
  --
  -- This function is likely only of interest if you wish to export metrics in
  -- a non-supported format for use with another monitoring service.
  collectMetrics :: MonadIO m => m [SampleGroup]
  collectMetrics = liftIO $ do
      registry <- STM.atomically $ STM.readTVar globalRegistry
      concat <$> sequence registry
  