-- | 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