    {-# LANGUAGE CPP #-}

    -- | This module provides remote monitoring of a running process over
    -- HTTP.  It can be used to run an HTTP server that provides both a
    -- web-based user interface and a machine-readable API (e.g. JSON.)
    -- The former can be used by a human to get an overview of what the
    -- program is doing and the latter can be used by automated monitoring
    -- tools.
    --
    -- Typical usage is to start the monitoring server at program startup
    --
    -- > main = do
    -- >     forkServer "localhost" 8000
    -- >     ...
    --
    -- and then periodically check the stats using a web browser or a
    -- command line tool (e.g. curl)
    --
    -- > $ curl -H "Accept: application/json" http://localhost:8000/
    module System.Remote.Monitoring
        (
          -- * Required configuration
          -- $configuration

          -- * Security considerations
          -- $security

          -- * REST API
          -- $api

          -- * The monitoring server
          Server
        , serverThreadId
        , forkServer

          -- * User-defined counters, gauges, and labels
          -- $userdefined
        , getCounter
        , getGauge
        , getLabel
        ) where

    import Control.Concurrent (ThreadId, forkIO)
    import qualified Data.ByteString as S
    import qualified Data.HashMap.Strict as M
    import Data.IORef (newIORef)
    import Prelude hiding (read)

    import System.Remote.Common

    import System.Remote.Snap

    -- $configuration
    --
    -- To use this module you must first enable GC statistics collection
    -- in the run-time system.  To enable GC statistics collection, either
    -- run your program with
    --
    -- > +RTS -T
    --
    -- or compile it with
    --
    -- > -with-rtsopts=-T
    --
    -- The runtime overhead of @-T@ is very small so it's safe to always
    -- leave it enabled.

    -- $security
    -- Be aware that if the server started by 'forkServer' is not bound to
    -- \"localhost\" (or equivalent) anyone on the network can access the
    -- monitoring server. Either make sure the network is secure or bind
    -- the server to \"localhost\".

    -- $api
    -- To use the machine-readable REST API, send an HTTP GET request to
    -- the host and port passed to 'forkServer'.  The following resources
    -- (i.e. URLs) are available:
    --
    -- [\/] JSON object containing all counters, gauges and labels.
    -- Counters, gauges, and labels are stored as nested objects under the
    -- @counters@, @gauges@, and @labels@ attributes, respectively.
    -- Content types: \"text\/html\" (default), \"application\/json\"
    --
    -- [\/combined] Flattened JSON object containing all counters, gauges,
    -- and labels.  Content types: \"application\/json\"
    --
    -- [\/counters] JSON object containing all counters.  Content types:
    -- \"application\/json\"
    --
    -- [\/counters/\<counter name\>] Value of a single counter, as a
    -- string.  The name should be UTF-8 encoded.  Content types:
    -- \"text\/plain\"
    --
    -- [\/gauges] JSON object containing all gauges.  Content types:
    -- \"application\/json\"
    --
    -- [\/gauges/\<gauge name\>] Value of a single gauge, as a string.
    -- The name should be UTF-8 encoded.  Content types: \"text\/plain\"
    --
    -- [\/labels] JSON object containing all labels.  Content types:
    -- \"application\/json\"
    --
    -- [\/labels/\<label name\>] Value of a single label, as a string.
    -- The name should be UTF-8 encoded.  Content types: \"text\/plain\"
    --
    -- Counters, gauges and labels are stored as attributes of the
    -- returned JSON objects, one attribute per counter, gauge or label.
    -- In addition to user-defined counters, gauges, and labels, the below
    -- built-in counters and gauges are also returned.  Furthermore, the
    -- top-level JSON object of any resource contains the
    -- @server_timestamp_millis@ attribute, which indicates the server
    -- time, in milliseconds, when the sample was taken.
    --
    -- Built-in counters:
    --
    -- [@bytes_allocated@] Total number of bytes allocated
    --
    -- [@num_gcs@] Number of garbage collections performed
    --
    -- [@num_bytes_usage_samples@] Number of byte usage samples taken
    --
    -- [@cumulative_bytes_used@] 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).
    --
    -- [@bytes_copied@] Number of bytes copied during GC
    --
    -- [@mutator_cpu_seconds@] CPU time spent running mutator threads.
    -- This does not include any profiling overhead or initialization.
    --
    -- [@mutator_wall_seconds@] Wall clock time spent running mutator
    -- threads.  This does not include initialization.
    --
    -- [@gc_cpu_seconds@] CPU time spent running GC
    --
    -- [@gc_wall_seconds@] Wall clock time spent running GC
    --
    -- [@cpu_seconds@] Total CPU time elapsed since program start
    --
    -- [@wall_seconds@] Total wall clock time elapsed since start
    --
    -- Built-in gauges:
    --
    -- [@max_bytes_used@] Maximum number of live bytes seen so far
    --
    -- [@current_bytes_used@] Current number of live bytes
    --
    -- [@current_bytes_slop@] Current number of bytes lost to slop
    --
    -- [@max_bytes_slop@] Maximum number of bytes lost to slop at any one time so far
    --
    -- [@peak_megabytes_allocated@] Maximum number of megabytes allocated
    --
    -- [@par_tot_bytes_copied@] 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.
    --
    -- [@par_avg_bytes_copied@] Deprecated alias for
    -- @par_tot_bytes_copied@.
    --
    -- [@par_max_bytes_copied@] Sum of number of bytes copied each GC by
    -- the most active GC thread each GC. The ratio of
    -- @par_tot_bytes_copied@ divided by @par_max_bytes_copied@ approaches
    -- 1 for a maximally sequential run and approaches the number of
    -- threads (set by the RTS flag @-N@) for a maximally parallel run.

    -- $userdefined
    -- The monitoring server can store and serve user-defined,
    -- integer-valued counters and gauges, and string-valued labels.  A
    -- counter is a monotonically increasing value (e.g. TCP connections
    -- established since program start.) A gauge is a variable value
    -- (e.g. the current number of concurrent connections.) A label is a
    -- free-form string value (e.g. exporting the command line arguments
    -- or host name.)  Each counter, gauge, and label is associated with a
    -- name, which is used when it is displayed in the UI or returned in a
    -- JSON object.
    --
    -- Even though it's technically possible to have a counter and a gauge
    -- with the same name, associated with the same server, it's not
    -- recommended as it might make it harder for clients to distinguish
    -- the two.
    --
    -- To create and use a counter, simply call 'getCounter' to create it
    -- and then call e.g. 'System.Remote.Counter.inc' or
    -- 'System.Remote.Counter.add' to modify its value.  Example:
    --
    -- > main = do
    -- >     handle <- forkServer "localhost" 8000
    -- >     counter <- getCounter "iterations" handle
    -- >     let loop n = do
    -- >             inc counter
    -- >             loop
    -- >     loop
    --
    -- To create a gauge, use 'getGauge' instead of 'getCounter' and then
    -- call e.g. 'System.Remote.Gauge.set' or
    -- 'System.Remote.Gauge.modify'.  Similar for labels.

    ------------------------------------------------------------------------
    -- * The monitoring server

    -- | The thread ID of the server.  You can kill the server by killing
    -- this thread (i.e. by throwing it an asynchronous exception.)
    serverThreadId :: Server -> ThreadId
    serverThreadId = threadId

    -- | Start an HTTP server in a new thread.  The server replies to GET
    -- requests to the given host and port.  The host argument can be
    -- either a numeric network address (dotted quad for IPv4,
    -- colon-separated hex for IPv6) or a hostname (e.g. \"localhost\".)
    -- The client can control the Content-Type used in responses by
    -- setting the Accept header.  At the moment three content types are
    -- available: \"application\/json\", \"text\/html\", and
    -- \"text\/plain\".
    forkServer :: S.ByteString  -- ^ Host to listen on (e.g. \"localhost\")
               -> Int           -- ^ Port to listen on (e.g. 8000)
               -> IO Server
    forkServer host port = do
        counters <- newIORef M.empty
        gauges <- newIORef M.empty
        labels <- newIORef M.empty
        tid <- forkIO $ startServer counters gauges labels host port
        return $! Server tid counters gauges labels