-- | This module provides the basics for instrumenting Haskell executables for
-- use with the <http://prometheus.io/ Prometheus> monitoring system.
module Prometheus (

-- * Registry

    registerIO
,   register
,   unsafeRegisterIO
,   unsafeRegister
,   unregisterAll
,   collectMetrics

-- * Exporting

,   exportMetricsAsText

-- * Metrics
--
-- | A metric represents a single value that is being monitored. For example
-- a metric could be the number of open files, the current CPU temperature, the
-- elapsed time of execution, and the latency of HTTP requests.
--
-- This module provides 4 built-in metric types: counters, gauges, summaries,
-- and metric vectors. These types of metrics should cover most typical use
-- cases. However, for more specialized use cases it  is also possible to write
-- custom metrics.

-- ** Counter
--
-- | A counter models a monotonically increasing value. It is the simplest
-- type of metric provided by this library.
--
-- A Counter is typically used to count requests served, tasks completed,
-- errors occurred, etc.
--
-- >>> myCounter <- register $ counter (Info "my_counter" "An example counter")
-- >>> replicateM_ 47 (incCounter myCounter)
-- >>> getCounter myCounter
-- 47.0
-- >>> void $ addCounter myCounter 10
-- >>> getCounter myCounter
-- 57.0

,   Counter
,   counter
,   incCounter
,   addCounter
,   unsafeAddCounter
,   addDurationToCounter
,   countExceptions
,   getCounter

-- ** Gauge
--
-- | A gauge models an arbitrary floating point value. There are operations to
-- set the value of a gauge as well as add and subtract arbitrary values.
--
-- >>> myGauge <- register $ gauge (Info "my_gauge" "An example gauge")
-- >>> setGauge myGauge 100 
-- >>> addGauge myGauge 50
-- >>> subGauge myGauge 25
-- >>> getGauge myGauge
-- 125.0

,   Gauge
,   gauge
,   incGauge
,   decGauge
,   addGauge
,   subGauge
,   setGauge
,   setGaugeToDuration
,   getGauge

-- ** Summaries and histograms
--
-- | An 'Observer' is a generic metric that captures observations of a
-- floating point value over time. Different implementations can store
-- and summarise these value in different ways.
--
-- The two main observers are summaries and histograms. A 'Summary' allows you
-- to get a precise estimate of a particular quantile, but cannot be meaningfully
-- aggregated across processes. A 'Histogram' packs requests into user-supplied
-- buckets, which /can/ be aggregated meaningfully, but provide much less precise
-- information on particular quantiles.

,   Observer(..)
,   observeDuration

-- *** Summary
--
-- | A summary is an 'Observer' that summarizes the observations as a count,
-- sum, and rank estimations. A typical use case for summaries is measuring
-- HTTP request latency.
--
-- >>> mySummary <- register $ summary (Info "my_summary" "") defaultQuantiles
-- >>> observe mySummary 0
-- >>> getSummary mySummary
-- [(1 % 2,0.0),(9 % 10,0.0),(99 % 100,0.0)]

,   Summary
,   Quantile
,   summary
,   defaultQuantiles
,   getSummary

-- *** Histogram
--
-- | A histogram captures observations of a floating point value over time
-- and stores those observations in a user-supplied histogram. A typical use case
-- for histograms is measuring HTTP request latency. Histograms are unlike
-- summaries in that they can be meaningfully aggregated across processes.
--
-- >>> myHistogram <- register $ histogram (Info "my_histogram" "") defaultBuckets
-- >>> observe myHistogram 0 
-- >>> getHistogram myHistogram
-- fromList [(5.0e-3,1),(1.0e-2,0),(2.5e-2,0),(5.0e-2,0),(0.1,0),(0.25,0),(0.5,0),(1.0,0),(2.5,0),(5.0,0),(10.0,0)]
,   Histogram
,   Bucket
,   histogram
,   defaultBuckets
,   exponentialBuckets
,   linearBuckets
,   getHistogram

-- ** Vector
--
-- | A vector models a collection of metrics that share the same name but are
-- partitioned across a set of dimensions.
--
-- >>> myVector <- register $ vector ("method", "code") $ counter (Info "http_requests" "")
-- >>> withLabel myVector ("GET", "200") incCounter 
-- >>> withLabel myVector ("GET", "200") incCounter 
-- >>> withLabel myVector ("GET", "404") incCounter 
-- >>> withLabel myVector ("POST", "200") incCounter 
-- >>> getVectorWith myVector getCounter 
-- [(("GET","200"),2.0),(("GET","404"),1.0),(("POST","200"),1.0)]
-- >>> exportMetricsAsText >>= Data.ByteString.Lazy.putStr
-- # HELP http_requests
-- # TYPE http_requests counter
-- http_requests{method="GET",code="200"} 2.0
-- http_requests{method="GET",code="404"} 1.0
-- http_requests{method="POST",code="200"} 1.0

,   Vector
,   vector
,   withLabel
,   removeLabel
,   clearLabels
,   getVectorWith

-- *** Labels
--
-- | The labels of a vector metric are types of the class Label. This module
-- defines all n-tupes of Strings for n <= 9 to be Labels. Additionally, the
-- type aliases LabelN is defined for each of these tuple types to make
-- specifying the types of vectors more concise.
--
-- >>> :{
-- >>> let myVector :: Metric (Vector Label3 Counter);
-- >>>     myVector = vector ("a", "b", "c") $ counter (Info "some_counter" "")
-- >>> :}

,   Label (..)
,   LabelPairs
,   Label0
,   Label1
,   Label2
,   Label3
,   Label4
,   Label5
,   Label6
,   Label7
,   Label8
,   Label9

-- ** Custom metrics
--
-- | Custom metrics can be created by directly creating a new 'Metric' type. There
-- are two parts of any metric, the handle and the collect method.
--
-- The handle is a value embedded in the metric that is intended to allow for
-- communication with the metric from instrumented code. For example, all of the
-- metrics provided by this library use a newtype wrapped TVar of some
-- underlying data type as their handle. When defining a new metric, it is
-- recommended that you use a newtype wrapper around your handle type as it will
-- allow users of your metric to succinctly identify your metric in type
-- signatures.
--
-- The collect method is responsible for serializing the current value of
-- a metric into a list of 'SampleGroup's.
--
-- The following is an example of a custom metric that models the current CPU
-- time. It uses a newtype wrapped unit as the handler type since it doesn't
-- need to maintain any state.
--
-- >>> :m +System.CPUTime
-- >>> :m +Data.ByteString.UTF8
-- >>> newtype CPUTime = MkCPUTime ()
-- >>> let info = Info "cpu_time" "The current CPU time"
-- >>> let toValue = Data.ByteString.UTF8.fromString . show
-- >>> let toSample = Sample "cpu_time" [] . toValue
-- >>> let toSampleGroup = (:[]) . SampleGroup info GaugeType . (:[]) . toSample
-- >>> let collectCPUTime = fmap toSampleGroup getCPUTime
-- >>> let cpuTimeMetric = Metric (return (MkCPUTime (), collectCPUTime))
-- >>> register cpuTimeMetric
-- >>> exportMetricsAsText >>= Data.ByteString.Lazy.putStr
-- # HELP cpu_time The current CPU time
-- # TYPE cpu_time gauge
-- cpu_time ...

-- * Instrumenting pure code
--
-- | Pure code can be instrumented through the use of the 'Monitor' monad and
-- 'MonitorT' monad transformer. These constructs work by queueing all
-- operations on metrics. In order for the operations to actually be performed,
-- the queue must be evaluated within the IO monad.
--
-- The following is a contrived example that defines an add function that
-- records the number of times it was invoked.
--
-- > add :: Int -> Int -> Monitor Int
--
-- Note that the changes to numAdds are not reflected until the updateMetrics
-- value has been evaluated in the IO monad.
--
-- >>> numAdds <- register $ counter (Info "num_adds" "The number of additions")
-- >>> let add x y = incCounter numAdds >> return (x + y)
-- >>> let (3, updateMetrics) = runMonitor $ (add 1 1) >>= (add 1)
-- >>> getCounter numAdds
-- 0.0
-- >>> updateMetrics
-- >>> getCounter numAdds
-- 2.0

,   MonadMonitor (..)
,   Monitor
,   runMonitor
,   MonitorT
,   runMonitorT

-- * Base data types

,   Info (..)
,   Metric (..)
,   Sample (..)
,   SampleGroup (..)
,   SampleType (..)
) where

import Prometheus.Export.Text
import Prometheus.Info
import Prometheus.Label
import Prometheus.Metric
import Prometheus.Metric.Counter
import Prometheus.Metric.Gauge
import Prometheus.Metric.Histogram
import Prometheus.Metric.Observer
import Prometheus.Metric.Summary
import Prometheus.Metric.Vector
import Prometheus.MonadMonitor
import Prometheus.Registry


-- $setup
-- >>> :module +Prometheus
-- >>> :module +Control.Monad
-- >>> :set -XOverloadedStrings
-- >>> unregisterAll