packages feed

prometheus-2.2.4: src/System/Metrics/Prometheus/Encode/Text/MetricId.hs

{-# LANGUAGE OverloadedStrings #-}

module System.Metrics.Prometheus.Encode.Text.MetricId (
    encodeHeader,
    encodeMetricId,
    encodeLabels,
    encodeName,
    textValue,
    encodeDouble,
    encodeInt,
    escape,
    newline,
    space,
) where

import Data.ByteString.Builder (
    Builder,
    byteString,
    char8,
    intDec,
 )
import Data.List (intersperse)
import Data.Monoid ((<>))
import Data.Text (Text, replace)
import Data.Text.Encoding (encodeUtf8)
import Data.Text.Lazy (toStrict)
import Data.Text.Lazy.Builder (toLazyText)
import Data.Text.Lazy.Builder.RealFloat (
    FPFormat (Generic),
    formatRealFloat,
 )
import Prelude hiding (null)

import System.Metrics.Prometheus.Metric (
    MetricSample (..),
    metricSample,
 )
import System.Metrics.Prometheus.MetricId (
    Labels (..),
    MetricId (..),
    Name (..),
    null,
    toList,
 )


encodeHeader :: MetricId -> MetricSample -> Builder
encodeHeader mid sample =
    "# TYPE " <> nm <> space <> encodeSampleType sample
  where
    -- <> "# HELP " <> nm <> space <> escape "help" <> newline <>
    nm = encodeName (name mid)


encodeSampleType :: MetricSample -> Builder
encodeSampleType =
    byteString
        . metricSample
            (const "counter")
            (const "gauge")
            (const "histogram")
            (const "summary")


encodeMetricId :: MetricId -> Builder
encodeMetricId mid = encodeName (name mid) <> encodeLabels (labels mid)


encodeName :: Name -> Builder
encodeName = text . unName


encodeLabels :: Labels -> Builder
encodeLabels ls
    | null ls = space
    | otherwise =
        openBracket
            <> (mconcat . intersperse comma . map encodeLabel $ toList ls)
            <> closeBracket


encodeLabel :: (Text, Text) -> Builder
encodeLabel (key, val) = text key <> equals <> quote <> text (escape val) <> quote


textValue :: RealFloat f => f -> Text
textValue x
    | isInfinite x && x > 0 = "+Inf"
    | isInfinite x && x < 0 = "-Inf"
    | isNaN x = "NaN"
    | otherwise = toStrict . toLazyText $ formatRealFloat Generic Nothing x


encodeDouble :: RealFloat f => f -> Builder
encodeDouble = text . textValue


encodeInt :: Int -> Builder
encodeInt = intDec


text :: Text -> Builder
text = byteString . encodeUtf8


escape :: Text -> Text
escape = replace "\n" "\\n" . replace "\"" "\\\"" . replace "\\" "\\\\"


space :: Builder
space = char8 ' '


newline :: Builder
newline = char8 '\n'


openBracket :: Builder
openBracket = char8 '{'


closeBracket :: Builder
closeBracket = char8 '}'


comma :: Builder
comma = char8 ','


equals :: Builder
equals = char8 '='


quote :: Builder
quote = char8 '"'