{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
module OpenTelemetry.Eventlog_Internal where
import Control.Monad.IO.Class
import Data.Bits
import qualified Data.ByteString as BS
import Data.ByteString.Builder
import qualified Data.ByteString.Char8 as BS8
import qualified Data.ByteString.Lazy as LBS
import Data.Char
import Data.Hashable
#if __GLASGOW_HASKELL__ < 808
import Debug.Trace.ByteString
#else
import Debug.Trace.Binary
#endif
import Data.Int
import Data.Unique
import Data.Word (Word64, Word8)
import OpenTelemetry.Metrics_Internal as MI
import OpenTelemetry.SpanContext
import Text.Printf
import Prelude hiding (span)
type ProcessLocalSpanSerialNumber = Word64
newtype SpanInFlight = SpanInFlight ProcessLocalSpanSerialNumber
deriving (Show, Eq, Hashable)
newtype MsgType = MsgType Word8
deriving (Show)
pattern BEGIN_SPAN, END_SPAN, TAG, EVENT, SET_PARENT_CONTEXT, SET_TRACE_ID, SET_SPAN_ID, DECLARE_INSTRUMENT, METRIC_CAPTURE :: MsgType
pattern BEGIN_SPAN = MsgType 1
pattern END_SPAN = MsgType 2
pattern TAG = MsgType 3
pattern EVENT = MsgType 4
pattern SET_PARENT_CONTEXT = MsgType 5
pattern SET_TRACE_ID = MsgType 6
pattern SET_SPAN_ID = MsgType 7
pattern DECLARE_INSTRUMENT = MsgType 8
pattern METRIC_CAPTURE = MsgType 9
{-# INLINE maxMsgLen #-}
maxMsgLen :: Int
maxMsgLen = shift 2 16
{-# INLINE otelMagic #-}
otelMagic :: Int
otelMagic = v .|. t .|. o
where
!v = shift 3 16
!t = shift (ord 'T') 8
!o = ord 'O'
{-# INLINE header #-}
header :: MsgType -> Builder
header (MsgType msgType) = word32LE $ fromIntegral h
where
!h = m .|. otelMagic
!m = shift ((fromIntegral msgType) :: Int) $ shift 3 3
headerSize :: Int
headerSize = fromIntegral $ LBS.length $ toLazyByteString (header TAG <> word64LE 0)
{-# INLINE checkSize #-}
checkSize :: Int -> m -> m
checkSize s next = do
let !exceed = s + headerSize - maxMsgLen
if exceed > 0
then error $ "eventlog message size exceed 64k by " ++ show exceed
else next
{-# INLINE nextLocalSpan #-}
nextLocalSpan :: MonadIO m => m SpanInFlight
nextLocalSpan = liftIO $ (SpanInFlight . fromIntegral . hashUnique) <$> newUnique
{-# INLINE nextInstrumentId #-}
nextInstrumentId :: MonadIO m => m InstrumentId
nextInstrumentId = liftIO $ (fromIntegral . hashUnique) <$> newUnique
#if __GLASGOW_HASKELL__ >= 808
{-# INLINE builder_beginSpan #-}
builder_beginSpan :: SpanInFlight -> BS.ByteString -> Builder
builder_beginSpan (SpanInFlight u) operation =
header BEGIN_SPAN <> word64LE u <> byteString operation
{-# INLINE builder_endSpan #-}
builder_endSpan :: SpanInFlight -> Builder
builder_endSpan (SpanInFlight u) = header END_SPAN <> word64LE u
{-# INLINE builder_key_value #-}
builder_key_value :: MsgType -> SpanInFlight -> BS.ByteString -> BS.ByteString -> Builder
builder_key_value msg (SpanInFlight u) k v =
let klen = fromIntegral $ BS.length k
vlen = fromIntegral $ BS.length v
in header msg <> word64LE u <> word32LE klen <> word32LE vlen <> byteString k <> byteString v
{-# INLINE builder_setTag #-}
builder_setTag :: SpanInFlight -> BS.ByteString -> BS.ByteString -> Builder
builder_setTag = builder_key_value TAG
{-# INLINE builder_addEvent #-}
builder_addEvent :: SpanInFlight -> BS.ByteString -> BS.ByteString -> Builder
builder_addEvent = builder_key_value EVENT
{-# INLINE builder_setParentSpanContext #-}
builder_setParentSpanContext :: SpanInFlight -> SpanContext -> Builder
builder_setParentSpanContext (SpanInFlight u) (SpanContext (SId sid) (TId tid)) =
header SET_PARENT_CONTEXT <> word64LE u <> word64LE sid <> word64LE tid
{-# INLINE builder_setTraceId #-}
builder_setTraceId :: SpanInFlight -> TraceId -> Builder
builder_setTraceId (SpanInFlight u) (TId tid) = header SET_TRACE_ID <> word64LE u <> word64LE tid
{-# INLINE builder_setSpanId #-}
builder_setSpanId :: SpanInFlight -> SpanId -> Builder
builder_setSpanId (SpanInFlight u) (SId sid) = header SET_SPAN_ID <> word64LE u <> word64LE sid
{-# INLINE builder_declareInstrument #-}
builder_declareInstrument :: Instrument s a m -> Builder
builder_declareInstrument instrument =
header DECLARE_INSTRUMENT <>
int8 (instrumentTag instrument) <>
word64LE (instrumentId instrument) <>
byteString (instrumentName instrument)
{-# INLINE builder_captureMetric #-}
builder_captureMetric :: InstrumentId -> Int -> Builder
builder_captureMetric iId v =
header METRIC_CAPTURE <>
word64LE iId <>
int64LE (fromIntegral v)
{-# INLINE traceBuilder #-}
traceBuilder :: MonadIO m => Builder -> m ()
traceBuilder = liftIO . traceBinaryEventIO . LBS.toStrict . toLazyByteString
#endif
beginSpan' :: SpanInFlight -> String -> String
beginSpan' (SpanInFlight u64) operation =
printf "ot2 begin span %d %s" u64 operation
endSpan' :: SpanInFlight -> String
endSpan' (SpanInFlight u64) = printf "ot2 end span %d" u64
setTag' :: SpanInFlight -> String -> BS8.ByteString -> String
setTag' (SpanInFlight u64) k v =
printf "ot2 set tag %d %s %s" u64 k (BS8.unpack v)
addEvent' :: SpanInFlight -> String -> BS8.ByteString -> String
addEvent' (SpanInFlight u64) k v =
printf "ot2 add event %d %s %s" u64 k (BS8.unpack v)
setParentSpanContext' :: SpanInFlight -> SpanContext -> String
setParentSpanContext' (SpanInFlight u64) (SpanContext (SId sid) (TId tid)) =
(printf "ot2 set parent %d %016x %016x" u64 tid sid)
setTraceId' :: SpanInFlight -> TraceId -> String
setTraceId' (SpanInFlight u64) (TId tid) =
printf "ot2 set traceid %d %016x" u64 tid
setSpanId' :: SpanInFlight -> SpanId -> String
setSpanId' (SpanInFlight u64) (SId sid) =
printf "ot2 set spanid %d %016x" u64 sid
createInstrument' :: MI.Instrument s a m -> String
createInstrument' i = printf "ot2 metric create %s %016x %s" (instrumentTagStr i) (instrumentId i) (BS8.unpack $ instrumentName i)
writeMetric' :: InstrumentId -> Int -> String
writeMetric' iid v = printf "ot2 metric capture %016x %s" iid (show v)
{-# INLINE instrumentTag #-}
instrumentTag :: Instrument s a m -> Int8
instrumentTag Counter {} = 1
instrumentTag UpDownCounter {} = 2
instrumentTag ValueRecorder {} = 3
instrumentTag SumObserver {} = 4
instrumentTag UpDownSumObserver {} = 5
instrumentTag ValueObserver {} = 6
{-# INLINE instrumentTagStr #-}
instrumentTagStr :: Instrument s a m -> String
instrumentTagStr Counter {} = "Counter"
instrumentTagStr UpDownCounter {} = "UpDownCounter"
instrumentTagStr ValueRecorder {} = "ValueRecorder"
instrumentTagStr SumObserver {} = "SumObserver"
instrumentTagStr UpDownSumObserver {} = "UpDownSumObserver"
instrumentTagStr ValueObserver {} = "ValueObserver"