instana-haskell-trace-sdk-0.10.0.0: test/shared/Instana/SDK/AgentStub/TraceRequest.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE OverloadedStrings #-}
module Instana.SDK.AgentStub.TraceRequest where
import Data.Aeson (FromJSON, ToJSON, Value (Object), (.:),
(.:?), (.=))
import qualified Data.Aeson as Aeson
import qualified Data.HashMap.Strict as HM
import Data.Map (Map)
import Data.Text (Text)
import GHC.Generics
type TraceRequest = [Span]
data From = From
{ entityId :: String
, agentUuid :: String
} deriving (Eq, Generic, Show)
instance FromJSON From where
parseJSON = Aeson.withObject "from" $
\fr ->
From
<$> fr .: "e" -- entityId
<*> fr .: "h" -- agent UUID/host ID
instance ToJSON From where
toJSON :: From -> Value
toJSON fr = Aeson.object
[ "e" .= entityId fr
, "h" .= agentUuid fr
]
data InstanaAncestor = InstanaAncestor
{ traceId :: Maybe String
, parentId :: Maybe String
} deriving (Eq, Generic, Show)
instance FromJSON InstanaAncestor where
parseJSON = Aeson.withObject "InstanaAncestor" $
\obj ->
InstanaAncestor
<$> obj .: "t"
<*> obj .: "p"
instance ToJSON InstanaAncestor where
toJSON :: InstanaAncestor -> Value
toJSON instanaAncestor = Aeson.object
[ "t" .= traceId instanaAncestor
, "p" .= parentId instanaAncestor
]
data Span =
Span
{ t :: String -- traceId
, s :: String -- spanId
, p :: Maybe String -- parentId
, n :: Text -- spanName
, ts :: Int -- timestamp
, d :: Int -- duration
, k :: Int -- kind
, ec :: Int -- errorCount
, ia :: Maybe InstanaAncestor -- instana ancestor
, tp :: Maybe Bool -- traceparent has been used for trace continuity
, lt :: Maybe String -- long trace ID
, crtp :: Maybe String -- correlation type
, crid :: Maybe String -- correlation id
, sy :: Maybe Bool -- synthetic
, spanData :: Aeson.Value -- spanData
, f :: Maybe From -- from
} deriving (Eq, Show, Generic)
instance FromJSON Span where
parseJSON = Aeson.withObject "span" $
\decodedObject ->
Span
<$> decodedObject .: "t"
<*> decodedObject .: "s"
<*> decodedObject .: "p"
<*> decodedObject .: "n"
<*> decodedObject .: "ts"
<*> decodedObject .: "d"
<*> decodedObject .: "k"
<*> decodedObject .: "ec"
<*> decodedObject .:? "ia"
<*> decodedObject .:? "tp"
<*> decodedObject .:? "lt"
<*> decodedObject .: "crtp"
<*> decodedObject .: "crid"
<*> decodedObject .:? "sy"
<*> decodedObject .: "data"
<*> decodedObject .: "f"
instance ToJSON Span where
toJSON sp = Aeson.object
[ "t" .= t sp
, "s" .= s sp
, "p" .= p sp
, "n" .= n sp
, "ts" .= ts sp
, "d" .= d sp
, "k" .= k sp
, "ec" .= ec sp
, "ia" .= ia sp
, "tp" .= tp sp
, "lt" .= lt sp
, "crtp" .= crtp sp
, "crid" .= crid sp
, "sy" .= sy sp
, "data" .= spanData sp
, "f" .= f sp
]
data SpanData = SpanData
{ httpAnnotations :: HttpAnnotations
}
deriving (Show)
instance FromJSON SpanData where
parseJSON = Aeson.withObject "Span Annotations" $
\obj ->
SpanData
<$> obj .: "http"
data HttpAnnotations = HttpAnnotations
{ method :: Maybe String
, host :: Maybe String
, url :: Maybe String
, params :: Maybe String
-- ("header",Object (fromList [("X-Response-Header-Downstream-To-App",String "Value 3"),("X-Request-Header-App-To-Downstream",String "Value 2")]))]))]),
, header :: Maybe (Map String String)
, status :: Maybe Int
}
deriving (Show)
instance FromJSON HttpAnnotations where
parseJSON = Aeson.withObject "HTTP Annotations" $
\obj ->
HttpAnnotations
<$> obj .:? "method"
<*> obj .:? "host"
<*> obj .:? "url"
<*> obj .:? "params"
<*> obj .:? "header"
<*> obj .:? "status"
readSdkName :: Span -> Maybe Text
readSdkName span_ =
let
value = extractProperty ["sdk", "name"] (spanData span_)
in
case value of
Just (Aeson.String sdkName) -> Just sdkName
_ -> Nothing
readService :: Span -> Maybe Text
readService span_ =
let
value = extractProperty ["service"] (spanData span_)
in
case value of
Just (Aeson.String service) -> Just service
_ -> Nothing
extractProperty :: [Text] -> Value -> Maybe Value
extractProperty [] value = Just value
extractProperty (key:keys) (Object o) = HM.lookup key o >>= extractProperty keys
extractProperty _ _ = Nothing