{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeOperators #-}
module Datadog.Jaeger where
import Data.Aeson
import Data.List (nub)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (mapMaybe, maybeToList)
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
import Servant.API
import qualified Datadog.Agent as Agent
type Dump = "dump" :> Get '[JSON] Jaeger
toJaeger :: [Agent.Trace] -> Jaeger
toJaeger traces = Jaeger $ mapMaybe traceToData traces
where
traceToData (Agent.Trace []) = Nothing
traceToData (Agent.Trace spans) =
let Agent.Span{..} = head spans
in Just $ Data
(TraceID . showt $ spanTraceId)
(spanToSpan <$> spans)
(M.fromList $ (\a -> (ProcessID a, Process a)) <$> services spans)
spanToSpan Agent.Span{..} =
let traceId = (TraceID . showt $ spanTraceId)
in Span (SpanID . showt $ spanId)
traceId
(Name spanName)
((Reference traceId) . (SpanID . showt) <$> maybeToList spanParentId)
(toInteger spanStart)
(toInteger spanDuration)
(mkTag <$> (concat $ M.toList <$> spanMeta))
(ProcessID spanService)
services spans = nub $ Agent.spanService <$> spans
showt = T.pack . show
mkTag (k, v) = Tag $ T.concat [k, ":", v]
newtype Jaeger = Jaeger [Data]
instance ToJSON Jaeger where
toJSON (Jaeger dat) = object ["data" .= dat]
newtype TraceID = TraceID Text deriving newtype (Eq, Ord, ToJSON)
newtype SpanID = SpanID Text deriving newtype (Eq, Ord, ToJSON)
newtype ProcessID = ProcessID Text deriving newtype (Eq, Ord, ToJSON, ToJSONKey)
newtype Name = Name Text deriving newtype (Eq, ToJSON)
data Data = Data
{ traceID :: TraceID
, spans :: [Span]
, processes :: Map ProcessID Process
} deriving (Generic, ToJSON)
data Process = Process
{ serviceName :: Text
} deriving (Generic, ToJSON)
data Span = Span
{ spanID :: SpanID
, traceID :: TraceID
, operationName :: Name
, references :: [Reference]
, startTime :: Integer
, duration :: Integer
, tags :: [Tag]
, processID :: ProcessID
} deriving (Generic, ToJSON)
data Reference = Reference
{ traceID :: TraceID
, spanID :: SpanID
} deriving (Eq, Ord, Generic, ToJSON)
newtype Tag = Tag
{ key :: Text
} deriving (Eq, Generic)
deriving anyclass (ToJSON)