Store gauges in addition to references

The gauages are not yet served by the server.
This commit is contained in:
Johan Tibell 2011-12-30 11:02:58 -08:00
parent 6bf34dc97e
commit 99539d4b23

View file

@ -29,9 +29,10 @@ module System.Remote.Monitoring
, serverThreadId
, forkServer
-- * User-defined counters
-- * User-defined counters and gauges
-- $counters
, getCounter
, getGauge
) where
import Control.Applicative ((<$>), (<|>))
@ -65,6 +66,8 @@ import System.FilePath ((</>))
import System.Remote.Counter (Counter)
import qualified System.Remote.Counter.Internal as Counter
import System.Remote.Gauge (Gauge)
import qualified System.Remote.Gauge.Internal as Gauge
-- $configuration
--
@ -154,11 +157,15 @@ import qualified System.Remote.Counter.Internal as Counter
-- Map of user-defined counters.
type Counters = M.HashMap T.Text Counter
-- Map of user-defined gauges.
type Gauges = M.HashMap T.Text Gauge
-- | A handle that can be used to control the monitoring server.
-- Created by 'forkServer'.
data Server = Server {
threadId :: {-# UNPACK #-} !ThreadId
, userCounters :: !(IORef Counters)
, userGauges :: !(IORef Gauges)
}
-- | The thread ID of the server. You can kill the server by killing
@ -178,8 +185,9 @@ forkServer :: S.ByteString -- ^ Host to listen on (e.g. \"localhost\")
-> IO Server
forkServer host port = do
counters <- newIORef M.empty
tid <- forkIO $ httpServe conf (monitor counters)
return $! Server tid counters
gauges <- newIORef M.empty
tid <- forkIO $ httpServe conf (monitor counters gauges)
return $! Server tid counters gauges
where conf = Config.setVerbose False $
Config.setErrorLog Config.ConfigNoLog $
Config.setAccessLog Config.ConfigNoLog $
@ -188,13 +196,13 @@ forkServer host port = do
Config.defaultConfig
------------------------------------------------------------------------
-- * User-defined counters
-- * User-defined counters and gauges
-- $counters
-- The monitoring server can store and serve user-defined,
-- integer-valued counters. Each counter is associated with a name,
-- which is used when the counter is displayed in the UI or returned
-- in a JSON object.
-- integer-valued counters and gauges. Each counter or gauge is
-- associated with a name, which is used when the counter or gauge is
-- displayed in the UI or returned in a JSON object.
--
-- To create and use a counter, simply call 'getCounter' to create it
-- and then call e.g. 'Counter.inc' or 'Counter.add' to modify its
@ -208,6 +216,32 @@ forkServer host port = do
-- > loop
-- > loop
class Ref r where
new :: IO r
instance Ref Counter where
new = Counter.new
instance Ref Gauge where
new = Gauge.new
-- | Lookup a 'Ref' by name in the given map. If no 'Ref' exists
-- under the given name, create a new one, insert it into the map and
-- return it.
getRef :: Ref r
=> T.Text -- ^ 'Ref' name
-> IORef (M.HashMap T.Text r) -- ^ Server that will serve the 'Ref'
-> IO r
getRef name mapRef = do
empty <- new
ref <- atomicModifyIORef mapRef $ \ m ->
case M.lookup name m of
Nothing -> let m' = M.insert name empty m
in (m', empty)
Just ref -> (m, ref)
return ref
{-# INLINABLE getRef #-}
-- | Return the counter associated with the given name and server.
-- Multiple calls to 'getCounter' with the same arguments will return
-- the same counter. The first time 'getCounter' is called for a
@ -216,14 +250,16 @@ forkServer host port = do
getCounter :: T.Text -- ^ Counter name
-> Server -- ^ Server that will serve the counter
-> IO Counter
getCounter name server = do
emptyCounter <- Counter.new
counter <- atomicModifyIORef (userCounters server) $ \ m ->
case M.lookup name m of
Nothing -> let m' = M.insert name emptyCounter m
in (m', emptyCounter)
Just counter -> (m, counter)
return counter
getCounter name server = getRef name (userCounters server)
-- | Return the gauge associated with the given name and server.
-- Multiple calls to 'getGauge' with the same arguments will return
-- the same gauge. The first time 'getGauge' is called for a given
-- name and server, a new, zero-initialized gauge will be returned.
getGauge :: T.Text -- ^ Gauge name
-> Server -- ^ Server that will serve the gauge
-> IO Gauge
getGauge name server = getRef name (userGauges server)
------------------------------------------------------------------------
-- * JSON serialization
@ -258,12 +294,14 @@ instance A.ToJSON Stats where
-- * HTTP request handler
-- | A handler that can be installed into an existing Snap application.
monitor :: IORef Counters -> Snap ()
monitor counters = do
monitor :: IORef Counters -> IORef Gauges -> Snap ()
monitor counters gauges = do
dataDir <- liftIO getDataDir
route [
("/", method GET (format "application/json" (serveAll counters)))
, ("/:counter", method GET (format "text/plain" (serveOne counters)))
("/", method GET (format "application/json"
(serveAll counters gauges)))
, ("/:counter", method GET (format "text/plain"
(serveOne counters gauges)))
]
<|> serveDirectory (dataDir </> "assets")
@ -282,8 +320,8 @@ format fmt action = do
_ -> pass
-- | Serve all counters, as a JSON object.
serveAll :: IORef Counters -> Snap ()
serveAll counters = do
serveAll :: IORef Counters -> IORef Gauges -> Snap ()
serveAll counters gauges = do
req <- getRequest
-- Workaround: Snap still matches requests to /foo to this handler
-- if the Accept header is "application/json", even though such
@ -305,8 +343,8 @@ serveAll counters = do
return (name, val)
-- | Serve a single counter, as plain text.
serveOne :: IORef Counters -> Snap ()
serveOne counters = do
serveOne :: IORef Counters -> IORef Gauges -> Snap ()
serveOne counters gauges = do
modifyResponse $ setContentType "text/plain"
m <- liftIO $ readIORef counters
req <- getRequest