Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions ExampleV3.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoFieldSelectors #-}

import Control.Concurrent (threadDelay)
import Control.Monad (forever)
import Data.Hashable (Hashable)
import GHC.Generics (Generic)
import qualified Prometheus.V3 as Prom
import qualified Prometheus.V3.Metric.Counter as Counter


type Status = Int


data Method = GET | POST
deriving (Eq, Generic, Hashable)
instance Prom.IsLabelValue Method where
toLabelValue = \case
GET -> "GET"
POST -> "POST"


fooCounter :: Prom.Counter
fooCounter = Counter.register "foo_total" "Number of foos"
{-# OPAQUE fooCounter #-}


requestsCounter :: Prom.Labelled (Method, Status) Prom.Counter
requestsCounter =
Prom.register
. Prom.withLabels ("method", "status") labels
$ Counter.new "requests_total" "Number of requests"
where
labels =
[ (method, status)
| method <- [GET, POST]
, status <- [200, 404, 500]
]
{-# OPAQUE requestsCounter #-}


data Request = Request
{ method :: Method
}


myHandler :: Request -> IO ()
myHandler req = do
status <- pure 200

Counter.inc fooCounter
Counter.inc (Counter.labels (req.method, status) requestsCounter)

-- Cache it, for a hot loop
c <- Counter.labels (req.method, status) requestsCounter
Counter.inc c


main :: IO ()
main = do
Prom.serveMetrics 9090 -- Serves metrics on separate thread
myHandler Request{method = GET}
myHandler Request{method = GET}

putStrLn ">>> Server running."
putStrLn ">>> Run `curl localhost:9090` to see metrics"
putStrLn ">>> Press Ctrl-C to exit"
forever $ threadDelay 10000000
36 changes: 36 additions & 0 deletions prometheus.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,40 @@ library
, System.Metrics.Prometheus.MetricId
, System.Metrics.Prometheus.Registry
, System.Metrics.Prometheus.RegistryT
, Prometheus.V3
, Prometheus.V3.Collector
, Prometheus.V3.Encode
, Prometheus.V3.Encode.Value
, Prometheus.V3.Http.Serve
, Prometheus.V3.Label
, Prometheus.V3.LabelName
, Prometheus.V3.Metric.Base
, Prometheus.V3.Metric.Counter
, Prometheus.V3.Metric.Gauge
, Prometheus.V3.Metric.Histogram
, Prometheus.V3.Metric.Labelled
, Prometheus.V3.MetricName
, Prometheus.V3.Name
, Prometheus.V3.Registry
, Prometheus.V3.Sample
, Prometheus.V3.Utils.FromRealFrac

build-depends: base >= 4.9 && < 5
, atomic-primops >= 0.8 && < 0.9
, bytestring >= 0.10 && < 0.13
, containers >= 0.5 && < 0.8
, hashable
, http-client >= 0.4 && < 0.8
, http-client-tls >= 0.3 && < 0.5
, http-types >= 0.8 && < 0.13
, network-uri >= 2.5 && < 2.7
, text >= 1.2 && < 2.2
, time
, transformers >= 0.4 && < 0.7
, wai >= 3.2 && < 3.3
, warp >= 3.2 && < 3.5
, unliftio
, unordered-containers

executable prometheus-example
main-is: Example.hs
Expand All @@ -115,6 +136,21 @@ executable prometheus-example
else
buildable: False

executable prometheus-example-v3
main-is: ExampleV3.hs
default-language: Haskell2010

ghc-options: -Wall -fwarn-tabs -fno-warn-unused-do-bind

build-depends: base
, hashable
, prometheus

if flag(buildexamples)
buildable: True
else
buildable: False

source-repository head
type: git
location: https://github.com/bitnomial/prometheus
96 changes: 96 additions & 0 deletions src/Prometheus/V3.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
-- | A new implementation of the API, following Prometheus's standard for writing
-- client libraries: https://prometheus.io/docs/instrumenting/writing_clientlibs/
--
-- When releasing prometheus-3.0:
-- * Rename this as Prometheus
-- * Remove old 'System.Metrics.Prometheus' modules
-- * Update README/Cabal description for new API
-- * Delete this preamble
--
-- == Usage
--
-- The recommended usage is to initialize the metrics as top-level global
-- variables in the same file as the functions they're instrumenting.
--
-- Note: globally registered metrics are only registered when it's used for the
-- first time, due to laziness.
--
-- @
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Prometheus.V3 qualified as Prom
-- import Prometheus.V3.Metric.Counter qualified as Counter
--
-- fooCounter :: Prom.Counter
-- fooCounter = Counter.register "foo_total" "Number of foos"
-- {-# OPAQUE fooCounter #-}
--
-- requestsCounter :: Prom.Labelled Prom.Counter
-- requestsCounter =
-- Prom.register
-- . Prom.withLabels ("method", "status") labels
-- $ Counter.new "requests_total" "Number of requests"
-- where
-- labels =
-- [ (method, status)
-- | method <- [GET, POST]
-- , status <- [200, 404, 500]
-- ]
-- {-# OPAQUE requestsCounter #-}
--
-- myHandler req = do
-- status <- run req
--
-- Counter.inc fooCounter
-- Counter.inc (Counter.labels (requestMethod req, status) requestsCounter)
--
-- -- Cache it, for a hot loop
-- c <- Counter.labels (requestMethod req, status) requestsCounter
-- Counter.inc c
--
-- main :: IO ()
-- main = do
-- Prom.serveMetrics 9090 -- Serves metrics on separate thread
-- @
--
-- Alternatively, you could initialize metrics in IO and pass it to your
-- functions as normal (e.g. Reader monad, as a function arg, etc.):
--
-- @
-- registry <- Prom.newRegistry
-- fooCounter <- Prom.registerTo registry $ Counter.new "foo_counter" ""
-- @
--
-- For advanced use-cases, you can also construct your own
-- 'Prometheus.V3.Collector.Collector' manually and register it to the registry.
module Prometheus.V3 (
-- * Metric types
Metric,
Counter,
Gauge,
Histogram,

-- * Labels
Labelled,
withLabels,
IsLabelValue (..),

-- * Registry
globalRegistry,
registerTo,
unregisterFrom,
register,

-- * Serving metrics
serveMetrics,
) where

import Prometheus.V3.Http.Serve
import Prometheus.V3.Label
import Prometheus.V3.Metric.Base
import Prometheus.V3.Metric.Counter (Counter)
import Prometheus.V3.Metric.Gauge (Gauge)
import Prometheus.V3.Metric.Histogram (Histogram)
import Prometheus.V3.Metric.Labelled
import Prometheus.V3.Registry

39 changes: 39 additions & 0 deletions src/Prometheus/V3/Collector.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NoFieldSelectors #-}

module Prometheus.V3.Collector (
Collector (..),

-- * Re-exports
MetricName,
Description,
MetricType (..),
Sample (..),
SampleValue (..),
defaultSample,
ToSampleValue (..),
) where

import Prometheus.V3.Metric.Base (
Description,
MetricName,
MetricType (..),
)
import Prometheus.V3.Sample (
Sample (..),
SampleValue (..),
ToSampleValue (..),
defaultSample,
)


data Collector = Collector
{ name :: MetricName
, description :: Description
, type_ :: MetricType
, getSamples :: IO [Sample]
}
Loading