packages feed

prodapi-0.1.0.0: src/Prod/Prometheus.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Prod.Prometheus (
    handlePrometheus,
    PrometheusApi,
    CORSAllowOrigin (..),
    PrometheusResult (..),
    initPrometheus,
    inc,
    obs,
    timeIt,
)
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.ByteString.Lazy (ByteString)
import Data.Coerce (coerce)
import Data.Text (Text)
import Data.Time.Clock (diffUTCTime, getCurrentTime)
import qualified Network.HTTP.Media as M
import Prometheus (Counter, Vector, exportMetricsAsText, register)
import qualified Prometheus as Prometheus
import Prometheus.Metric.GHC (GHCMetrics, ghcMetrics)
import Servant
import Servant.Server (Handler, Server)

newtype PrometheusResult = PrometheusResult {toLBS :: ByteString}

instance MimeRender PlainText PrometheusResult where
    mimeRender _ = toLBS

type PrometheusApi =
    Summary "Prometheus metrics"
        :> "metrics"
        :> Get '[PlainText] (Headers '[Header "Access-Control-Allow-Origin" CORSAllowOrigin] PrometheusResult)

newtype CORSAllowOrigin = CORSAllowOrigin Text
    deriving (ToHttpApiData)

handlePrometheus :: CORSAllowOrigin -> Server PrometheusApi
handlePrometheus corsAllow = handleMetrics
  where
    handleMetrics :: Handler (Headers '[Header "Access-Control-Allow-Origin" CORSAllowOrigin] PrometheusResult)
    handleMetrics = do
        metrics <- liftIO $ exportMetricsAsText
        pure $ addHeader (coerce corsAllow) $ PrometheusResult metrics

initPrometheus :: IO GHCMetrics
initPrometheus = register ghcMetrics

inc ::
    (MonadIO m) =>
    (a -> Vector Text Counter) ->
    Text ->
    a ->
    m ()
inc f s cnts =
    liftIO $ Prometheus.withLabel (f cnts) s Prometheus.incCounter

obs ::
    (MonadIO m) =>
    (a -> Prometheus.Summary) ->
    Double ->
    a ->
    m ()
obs f v cnts =
    liftIO $ Prometheus.observe (f cnts) v

timeIt ::
    (MonadIO m) =>
    (a -> Prometheus.Summary) ->
    a ->
    m b ->
    m b
timeIt f cnts action = do
    t0 <- liftIO $ getCurrentTime
    !ret <- action
    t1 <- liftIO $ getCurrentTime
    obs f (fromRational $ toRational $ diffUTCTime t1 t0) cnts
    pure ret