opentelemetry-0.2.0: src/OpenTelemetry/Common.hs
{-# LANGUAGE OverloadedStrings #-}
module OpenTelemetry.Common where
import qualified Data.HashMap.Strict as HM
import Data.Hashable
import GHC.Generics
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty ((:|)), (<|))
import qualified Data.Text as T
import Data.Word
import System.Clock
import Text.Printf
newtype TraceId = TId Word64
deriving (Eq, Generic)
deriving (Hashable) via Word64
instance Show TraceId where
show (TId tid) = printf "TraceId %x" tid
newtype SpanId = SId Word64
deriving (Eq, Generic)
deriving (Hashable) via Word64
instance Show SpanId where
show (SId sid) = printf "SpanId %x" sid
type Timestamp = Word64
data Tracer threadId
= Tracer
{ tracerSpanStacks :: !(HM.HashMap threadId (NE.NonEmpty Span))
}
deriving (Eq, Show)
tracerPushSpan :: (Eq tid, Hashable tid) => Tracer tid -> tid -> Span -> Tracer tid
tracerPushSpan t@(Tracer {..}) tid sp =
case HM.lookup tid tracerSpanStacks of
Nothing ->
let !stacks = HM.insert tid (sp :| []) tracerSpanStacks
in Tracer stacks
Just sps ->
let !stacks = HM.insert tid (sp <| sps) tracerSpanStacks
in t { tracerSpanStacks = stacks }
tracerPopSpan :: (Eq tid, Hashable tid) => Tracer tid -> tid -> (Maybe Span, Tracer tid)
tracerPopSpan t@(Tracer {..}) tid =
case HM.lookup tid tracerSpanStacks of
Nothing -> (Nothing, t)
Just (sp :| sps) ->
let stacks =
case NE.nonEmpty sps of
Nothing -> HM.delete tid tracerSpanStacks
Just sps' -> HM.insert tid sps' tracerSpanStacks
in (Just sp, Tracer stacks)
tracerGetCurrentActiveSpan :: (Hashable tid, Eq tid) => Tracer tid -> tid -> Maybe Span
tracerGetCurrentActiveSpan (Tracer stacks) tid =
case HM.lookup tid stacks of
Nothing -> Nothing
Just (sp NE.:| _) -> Just sp
createTracer :: (Hashable tid, Eq tid) => IO (Tracer tid)
createTracer = pure $ Tracer mempty
data SpanContext = SpanContext !SpanId !TraceId
deriving (Show, Eq, Generic)
data TagValue
= StringTagValue !T.Text
| BoolTagValue !Bool
| IntTagValue !Int
| DoubleTagValue !Double
deriving (Eq, Show)
class ToTagValue a where
toTagValue :: a -> TagValue
instance ToTagValue String where
toTagValue = StringTagValue . T.pack
instance ToTagValue T.Text where
toTagValue = StringTagValue
instance ToTagValue Bool where
toTagValue = BoolTagValue
instance ToTagValue Int where
toTagValue = IntTagValue
data Span
= Span
{ spanContext :: {-# UNPACK #-} !SpanContext,
spanOperation :: T.Text,
spanStartedAt :: !Timestamp,
spanFinishedAt :: !Timestamp,
spanTags :: !(HM.HashMap T.Text TagValue),
spanEvents :: [SpanEvent],
spanStatus :: !SpanStatus,
spanParentId :: Maybe SpanId
}
deriving (Show, Eq)
spanTraceId :: Span -> TraceId
spanTraceId Span {spanContext = SpanContext _ tid} = tid
spanId :: Span -> SpanId
spanId Span {spanContext = SpanContext sid _} = sid
data SpanEvent = SpanEvent
{ spanEventTimestamp :: !Timestamp
, spanEventKey :: !T.Text
, spanEventValue :: !T.Text
} deriving (Show, Eq)
data SpanStatus = OK
deriving (Show, Eq)
data Event
= Event T.Text Timestamp
deriving (Show, Eq)
data SpanProcessor
= SpanProcessor
{ onStart :: Span -> IO (),
onEnd :: Span -> IO ()
}
data ExportResult
= ExportSuccess
| ExportFailedRetryable
| ExportFailedNotRetryable
deriving (Show, Eq)
data Exporter thing
= Exporter
{ export :: [thing] -> IO ExportResult,
shutdown :: IO ()
}
data OpenTelemetryConfig
= OpenTelemetryConfig
{ otcSpanExporter :: Exporter Span
}
noopExporter :: Exporter whatever
noopExporter = Exporter (const (pure ExportFailedNotRetryable)) (pure ())
now64 :: IO Timestamp
now64 = do
TimeSpec secs nsecs <- getTime Realtime
pure $! fromIntegral secs * 1_000_000_000 + fromIntegral nsecs