packages feed

opentelemetry-0.6.0: src/OpenTelemetry/Metrics_Internal.hs

{-|

This is an internal module. The public interface is re-exported by "OpenTelemetry.Eventlog"

This module implements the instruments of the metrics portion of the
OpenTelemetry API. It is reexported by "OpenTelemetry.Eventlog" and should be
used by importing that.

The way to use the 'Instrument' type is throught the 'add', 'record' or
'observe' functions (depending on the instrument type) which capture metrics on
a given instrument.

Usage:

@
import OpenTelemetry.Eventlog

aCounter :: Counter
aCounter = Counter "myCounter"

anObserver :: ValueObserver
anObserver = ValueObserver "myObserver"

main :: IO ()
main = do
  add aCounter 3
  record anObserver 40
@

-}

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}

module OpenTelemetry.Metrics_Internal
  ( Instrument(..)
  , SomeInstrument(..)
  -- * Synonyms for specific types of Instrument
  , Counter
  , UpDownCounter
  , ValueRecorder
  , SumObserver
  , UpDownSumObserver
  , ValueObserver
  -- * Used for indexing Instrument. All possible combinations are covered
  , Synchronicity(..)
  , Additivity(..)
  , Monotonicity(..)
  , InstrumentName
  , InstrumentId
  , instrumentName
  , instrumentId
  ) where

import Data.Hashable (Hashable(..))
import Data.ByteString as BS
import Data.Word

data Synchronicity = Synchronous | Asynchronous
data Additivity = Additive | NonAdditive
data Monotonicity = Monotonic | NonMonotonic

type InstrumentName = BS.ByteString
type InstrumentId = Word64

type Counter           = Instrument 'Synchronous  'Additive    'Monotonic
type UpDownCounter     = Instrument 'Synchronous  'Additive    'NonMonotonic
type ValueRecorder     = Instrument 'Synchronous  'NonAdditive 'NonMonotonic
type SumObserver       = Instrument 'Asynchronous 'Additive    'Monotonic
type UpDownSumObserver = Instrument 'Asynchronous 'Additive    'NonMonotonic
type ValueObserver     = Instrument 'Asynchronous 'NonAdditive 'NonMonotonic

-- TODO: Support tags

-- | An OpenTelemetry instrument as defined in the OpenTelemetry Metrics API
-- (<https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/metrics/api.md>)
data Instrument (s :: Synchronicity) (a :: Additivity) (m :: Monotonicity) where
  Counter           :: InstrumentName -> InstrumentId -> Counter
  UpDownCounter     :: InstrumentName -> InstrumentId -> UpDownCounter
  ValueRecorder     :: InstrumentName -> InstrumentId -> ValueRecorder
  SumObserver       :: InstrumentName -> InstrumentId -> SumObserver
  UpDownSumObserver :: InstrumentName -> InstrumentId -> UpDownSumObserver
  ValueObserver     :: InstrumentName -> InstrumentId -> ValueObserver

-- | Existential wrapper for 'Instrument'. Use when the exact type of Instrument does not matter.
data SomeInstrument = forall s a m. SomeInstrument (Instrument s a m)

instrumentName :: Instrument s a m -> InstrumentName
instrumentName (Counter n _) = n
instrumentName (UpDownCounter n _) = n
instrumentName (ValueRecorder n _) = n
instrumentName (SumObserver n _) = n
instrumentName (UpDownSumObserver n _) = n
instrumentName (ValueObserver n _) = n

instrumentId :: Instrument s a m -> InstrumentId
instrumentId (Counter _ i) = i
instrumentId (UpDownCounter _ i) = i
instrumentId (ValueRecorder _ i) = i
instrumentId (SumObserver _ i) = i
instrumentId (UpDownSumObserver _ i) = i
instrumentId (ValueObserver _ i) = i

deriving instance Show (Instrument s a m)
deriving instance Eq (Instrument s a m)

instance Show SomeInstrument where
  show (SomeInstrument i) = show i

instance Eq SomeInstrument where
  (SomeInstrument i1) == (SomeInstrument i2) = case (i1, i2) of
    (Counter s1 id1, Counter s2 id2) -> s1 == s2 && id1 == id2
    (UpDownCounter s1 id1, UpDownCounter s2 id2) -> s1 == s2 && id1 == id2
    (ValueRecorder s1 id1, ValueRecorder s2 id2) -> s1 == s2 && id1 == id2
    (SumObserver s1 id1, SumObserver s2 id2) -> s1 == s2 && id1 == id2
    (UpDownSumObserver s1 id1, UpDownSumObserver s2 id2) -> s1 == s2 && id1 == id2
    (ValueObserver s1 id1, ValueObserver s2 id2) -> s1 == s2 && id1 == id2
    (_, _) -> False

instance Hashable (Instrument s a m) where
  hashWithSalt s i = s `hashWithSalt` (constructorIdx i) `hashWithSalt` (instrumentName i)
    where
      constructorIdx :: Instrument s a m -> Int
      constructorIdx Counter{} = 0
      constructorIdx UpDownCounter{} = 1
      constructorIdx ValueRecorder{} = 2
      constructorIdx SumObserver{} = 3
      constructorIdx UpDownSumObserver{} = 4
      constructorIdx ValueObserver{} = 5

instance Hashable SomeInstrument where
  hashWithSalt s (SomeInstrument i) = hashWithSalt s i