module Ermine.Monitor
( withMonitor
, Monitor(..)
, HasMonitor(..)
, Gauge(..)
, gauge, gaugeM
, Counter(..)
, counter, counterM
, Label(..)
, label, labelM
, Server
, withServer
, forkServer
, module Ermine.Monitor.Combinators
, module Ermine.Monitor.Options
) where
import Control.Exception
import Control.Lens hiding (Setting)
import Control.Monad.Trans
import Control.Monad.Reader
import Data.ByteString.Lens
import Data.Foldable as F
import Data.Int
import Data.Text
import Ermine.Monitor.Combinators
import Ermine.Monitor.Exception
import Ermine.Monitor.Options
import System.Process
import System.Remote.Monitoring
import qualified System.Remote.Gauge as G
import qualified System.Remote.Counter as C
import qualified System.Remote.Label as L
data Monitor = Monitor
{ __monitorOptions :: MonitorOptions
, _monitorServer :: Maybe Server
}
makeClassy ''Monitor
instance HasMonitorOptions Monitor where
monitorOptions = _monitorOptions
withServer :: HasMonitor t => t -> (Server -> IO ()) -> IO ()
withServer t = F.forM_ $ t^.monitorServer
newtype Gauge = Gauge { runGauge :: Maybe G.Gauge }
newtype Label = Label { runLabel :: Maybe L.Label }
newtype Counter = Counter { runCounter :: Maybe C.Counter }
instance Setting Label Text where
assign (Label t) a = liftIO $ maybe (return ()) (L.set ?? a) t
instance Updating Label Text where
update (Label t) f = liftIO $ maybe (return ()) (L.modify f) t
instance Setting Gauge Int64 where
assign (Gauge t) a = liftIO $ maybe (return ()) (G.set ?? a) t
instance Gauged Gauge Int64 where
dec (Gauge t) = liftIO $ maybe (return ()) G.dec t
sub (Gauge t) i = liftIO $ maybe (return ()) (G.add ?? negate i) t
instance Incremental Gauge where
inc (Gauge t) = liftIO $ maybe (return ()) G.inc t
add (Gauge t) i = liftIO $ maybe (return ()) (G.add ?? i) t
instance Incremental Counter where
inc (Counter t) = liftIO $ maybe (return ()) C.inc t
add (Counter t) i = liftIO $ maybe (return ()) (C.add ?? i) t
gauge :: (MonadIO m, HasMonitor t) => Text -> t -> m Gauge
gauge = runReaderT . gaugeM
counter :: (MonadIO m, HasMonitor t) => Text -> t -> m Counter
counter = runReaderT . counterM
label :: (MonadIO m, HasMonitor t) => Text -> t -> m Label
label = runReaderT . labelM
gaugeM :: (MonadIO m, MonadReader t m, HasMonitor t) => Text -> m Gauge
gaugeM l = view monitorServer >>= maybe (return $ Gauge Nothing) (liftIO . fmap (Gauge . Just) . getGauge l)
counterM :: (MonadIO m, MonadReader t m, HasMonitor t) => Text -> m Counter
counterM l = view monitorServer >>= maybe (return $ Counter Nothing) (liftIO . fmap (Counter . Just) . getCounter l)
labelM :: (MonadIO m, MonadReader t m, HasMonitor t) => Text -> m Label
labelM t = view monitorServer >>= maybe (return $ Label Nothing) (liftIO . fmap (Label . Just) . getLabel t)
withMonitor :: HasMonitorOptions t => t -> (Monitor -> IO a) -> IO a
withMonitor t k
| t^.monitorEnabled = do
server <- forkServer (t^.monitorHost.packedChars) (t^.monitorPort)
let uri = monitorUri t
putStrLn $ "Monitoring enabled at " ++ uri
when (t^.monitorOpen) $ do
_ <- system $ "/usr/bin/open " ++ uri
return ()
k (Monitor (t^.monitorOptions) $ Just server) `finally` throwTo (serverThreadId server) ShutdownMonitor
| otherwise = k $ Monitor (t^.monitorOptions) Nothing