{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE InstanceSigs      #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module      : Instana.SDK.Internal.Context
Description : Internal representations of a span with all values set, ready to
              be sent to the agent (over the wire, hence the name).
-}
module Instana.SDK.Internal.WireSpan
  ( QueuedSpan(..)
  , WireSpan(..)
  , SpanKind(..)
  ) where


import           Control.Applicative       ((<|>))
import           Data.Aeson                (FromJSON, ToJSON, Value, (.:), (.=))
import qualified Data.Aeson                as Aeson
import           Data.Aeson.Types          (Parser)
import           Data.Text                 (Text)
import           GHC.Generics

import           Instana.SDK.Internal.Id   (Id)
import qualified Instana.SDK.Internal.Id   as Id
import           Instana.SDK.Span.SpanData (SpanData)
import qualified Instana.SDK.Span.SpanData as SpanData


-- |Direction of the call.
data SpanKind =
    -- |The monitored componenent receives a call.
    Entry
    -- |The monitored componenent calls something else.
  | Exit
    -- |An additional annotation that is added to the trace while a traced call
    -- is being processed.
  | Intermediate
  deriving (SpanKind -> SpanKind -> Bool
(SpanKind -> SpanKind -> Bool)
-> (SpanKind -> SpanKind -> Bool) -> Eq SpanKind
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SpanKind -> SpanKind -> Bool
$c/= :: SpanKind -> SpanKind -> Bool
== :: SpanKind -> SpanKind -> Bool
$c== :: SpanKind -> SpanKind -> Bool
Eq, (forall x. SpanKind -> Rep SpanKind x)
-> (forall x. Rep SpanKind x -> SpanKind) -> Generic SpanKind
forall x. Rep SpanKind x -> SpanKind
forall x. SpanKind -> Rep SpanKind x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SpanKind x -> SpanKind
$cfrom :: forall x. SpanKind -> Rep SpanKind x
Generic, Int -> SpanKind -> ShowS
[SpanKind] -> ShowS
SpanKind -> String
(Int -> SpanKind -> ShowS)
-> (SpanKind -> String) -> ([SpanKind] -> ShowS) -> Show SpanKind
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SpanKind] -> ShowS
$cshowList :: [SpanKind] -> ShowS
show :: SpanKind -> String
$cshow :: SpanKind -> String
showsPrec :: Int -> SpanKind -> ShowS
$cshowsPrec :: Int -> SpanKind -> ShowS
Show)


instance FromJSON SpanKind where
  parseJSON :: Value -> Parser SpanKind
  parseJSON :: Value -> Parser SpanKind
parseJSON = String
-> (Scientific -> Parser SpanKind) -> Value -> Parser SpanKind
forall a. String -> (Scientific -> Parser a) -> Value -> Parser a
Aeson.withScientific "span kind string" ((Scientific -> Parser SpanKind) -> Value -> Parser SpanKind)
-> (Scientific -> Parser SpanKind) -> Value -> Parser SpanKind
forall a b. (a -> b) -> a -> b
$
    \k :: Scientific
k ->
      case Scientific
k of
        -- (1=entry, 2=exit, 3=local/intermediate)
        1 -> SpanKind -> Parser SpanKind
forall (m :: * -> *) a. Monad m => a -> m a
return SpanKind
Entry
        2 -> SpanKind -> Parser SpanKind
forall (m :: * -> *) a. Monad m => a -> m a
return SpanKind
Exit
        3 -> SpanKind -> Parser SpanKind
forall (m :: * -> *) a. Monad m => a -> m a
return SpanKind
Intermediate
        _              ->
          String -> Parser SpanKind
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "expected numeric span kind (1, 2, or 3)."


instance ToJSON SpanKind where
  toJSON :: SpanKind -> Value
  toJSON :: SpanKind -> Value
toJSON k :: SpanKind
k =
    case SpanKind
k of
      Entry        -> Scientific -> Value
Aeson.Number 1
      Exit         -> Scientific -> Value
Aeson.Number 2
      Intermediate -> Scientific -> Value
Aeson.Number 3


-- |The `from` part of the span.
data From = From
  { From -> String
entityId :: String
  , From -> Text
hostId   :: Text
  } deriving (From -> From -> Bool
(From -> From -> Bool) -> (From -> From -> Bool) -> Eq From
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: From -> From -> Bool
$c/= :: From -> From -> Bool
== :: From -> From -> Bool
$c== :: From -> From -> Bool
Eq, (forall x. From -> Rep From x)
-> (forall x. Rep From x -> From) -> Generic From
forall x. Rep From x -> From
forall x. From -> Rep From x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep From x -> From
$cfrom :: forall x. From -> Rep From x
Generic, Int -> From -> ShowS
[From] -> ShowS
From -> String
(Int -> From -> ShowS)
-> (From -> String) -> ([From] -> ShowS) -> Show From
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [From] -> ShowS
$cshowList :: [From] -> ShowS
show :: From -> String
$cshow :: From -> String
showsPrec :: Int -> From -> ShowS
$cshowsPrec :: Int -> From -> ShowS
Show)


instance FromJSON From where
  parseJSON :: Value -> Parser From
parseJSON = String -> (Object -> Parser From) -> Value -> Parser From
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject "from" ((Object -> Parser From) -> Value -> Parser From)
-> (Object -> Parser From) -> Value -> Parser From
forall a b. (a -> b) -> a -> b
$
    \f :: Object
f ->
      String -> Text -> From
From
        (String -> Text -> From) -> Parser String -> Parser (Text -> From)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
f Object -> Text -> Parser String
forall a. FromJSON a => Object -> Text -> Parser a
.: "e" -- entityId
        Parser (Text -> From) -> Parser Text -> Parser From
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
f Object -> Text -> Parser Text
forall a. FromJSON a => Object -> Text -> Parser a
.: "h" -- host ID/agent UUID


instance ToJSON From where
  toJSON :: From -> Value
  toJSON :: From -> Value
toJSON f :: From
f = [Pair] -> Value
Aeson.object
    [ "e" Text -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= From -> String
entityId From
f
    , "h" Text -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= From -> Text
hostId From
f
    ]


-- |A representation of the span with all its data, except for attributes that
-- are constant per Haskell process (pid/entityId and agent UUID/host ID). This
-- is a preliminary representation of what will be send to the agent later
-- (after adding the aforementioned per-process/static attributes). Values of
-- this type are stored in the spanQueue in Instana.SDK.Internal.Context after
-- they have been completed.
data QueuedSpan = QueuedSpan
  { QueuedSpan -> Id
traceId         :: Id
  , QueuedSpan -> Id
spanId          :: Id
  , QueuedSpan -> Maybe Id
parentId        :: Maybe Id
  , QueuedSpan -> Text
spanName        :: Text
  , QueuedSpan -> Int
timestamp       :: Int
  , QueuedSpan -> Int
duration        :: Int
  , QueuedSpan -> SpanKind
kind            :: SpanKind
  , QueuedSpan -> Int
errorCount      :: Int
  , QueuedSpan -> Maybe Text
serviceName     :: Maybe Text
  , QueuedSpan -> Maybe Text
correlationType :: Maybe Text
  , QueuedSpan -> Maybe Text
correlationId   :: Maybe Text
  , QueuedSpan -> Maybe Bool
tpFlag          :: Maybe Bool
  , QueuedSpan -> Maybe (String, String)
instanaAncestor :: Maybe (String, String)
  , QueuedSpan -> Maybe Bool
synthetic       :: Maybe Bool
  , QueuedSpan -> SpanData
spanData        :: SpanData
  } deriving (QueuedSpan -> QueuedSpan -> Bool
(QueuedSpan -> QueuedSpan -> Bool)
-> (QueuedSpan -> QueuedSpan -> Bool) -> Eq QueuedSpan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: QueuedSpan -> QueuedSpan -> Bool
$c/= :: QueuedSpan -> QueuedSpan -> Bool
== :: QueuedSpan -> QueuedSpan -> Bool
$c== :: QueuedSpan -> QueuedSpan -> Bool
Eq, (forall x. QueuedSpan -> Rep QueuedSpan x)
-> (forall x. Rep QueuedSpan x -> QueuedSpan) -> Generic QueuedSpan
forall x. Rep QueuedSpan x -> QueuedSpan
forall x. QueuedSpan -> Rep QueuedSpan x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep QueuedSpan x -> QueuedSpan
$cfrom :: forall x. QueuedSpan -> Rep QueuedSpan x
Generic, Int -> QueuedSpan -> ShowS
[QueuedSpan] -> ShowS
QueuedSpan -> String
(Int -> QueuedSpan -> ShowS)
-> (QueuedSpan -> String)
-> ([QueuedSpan] -> ShowS)
-> Show QueuedSpan
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [QueuedSpan] -> ShowS
$cshowList :: [QueuedSpan] -> ShowS
show :: QueuedSpan -> String
$cshow :: QueuedSpan -> String
showsPrec :: Int -> QueuedSpan -> ShowS
$cshowsPrec :: Int -> QueuedSpan -> ShowS
Show)


-- |Combines the actual span data with static per-process data (PID,
-- agent UUID). This is the final value that will be sent to the agent.
data WireSpan = WireSpan
  { WireSpan -> QueuedSpan
queuedSpan        :: QueuedSpan
  , WireSpan -> String
pid               :: String
  , WireSpan -> Text
agentUuid         :: Text
  , WireSpan -> Maybe Text
serviceNameConfig :: Maybe Text
  } deriving (WireSpan -> WireSpan -> Bool
(WireSpan -> WireSpan -> Bool)
-> (WireSpan -> WireSpan -> Bool) -> Eq WireSpan
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: WireSpan -> WireSpan -> Bool
$c/= :: WireSpan -> WireSpan -> Bool
== :: WireSpan -> WireSpan -> Bool
$c== :: WireSpan -> WireSpan -> Bool
Eq, (forall x. WireSpan -> Rep WireSpan x)
-> (forall x. Rep WireSpan x -> WireSpan) -> Generic WireSpan
forall x. Rep WireSpan x -> WireSpan
forall x. WireSpan -> Rep WireSpan x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep WireSpan x -> WireSpan
$cfrom :: forall x. WireSpan -> Rep WireSpan x
Generic, Int -> WireSpan -> ShowS
[WireSpan] -> ShowS
WireSpan -> String
(Int -> WireSpan -> ShowS)
-> (WireSpan -> String) -> ([WireSpan] -> ShowS) -> Show WireSpan
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [WireSpan] -> ShowS
$cshowList :: [WireSpan] -> ShowS
show :: WireSpan -> String
$cshow :: WireSpan -> String
showsPrec :: Int -> WireSpan -> ShowS
$cshowsPrec :: Int -> WireSpan -> ShowS
Show)


instance ToJSON WireSpan where
  toJSON :: WireSpan -> Value
  toJSON :: WireSpan -> Value
toJSON wireSpan :: WireSpan
wireSpan =
    let
      tId :: Id
tId = QueuedSpan -> Id
traceId QueuedSpan
span_
      longTraceId :: Maybe String
longTraceId =
        case QueuedSpan -> SpanKind
kind QueuedSpan
span_ of
          Entry -> Id -> Maybe String
Id.longTraceId Id
tId
          _     -> Maybe String
forall a. Maybe a
Nothing
      span_ :: QueuedSpan
span_ = WireSpan -> QueuedSpan
queuedSpan WireSpan
wireSpan
      pid_ :: String
pid_ = WireSpan -> String
pid WireSpan
wireSpan
      agentUuid_ :: Text
agentUuid_ = WireSpan -> Text
agentUuid WireSpan
wireSpan
      serviceNameConfig_ :: Maybe Text
serviceNameConfig_ = WireSpan -> Maybe Text
serviceNameConfig WireSpan
wireSpan
      ia :: Maybe Value
ia =
        case QueuedSpan -> Maybe (String, String)
instanaAncestor QueuedSpan
span_ of
          Just (iaTId :: String
iaTId, iaPId :: String
iaPId) ->
            Value -> Maybe Value
forall a. a -> Maybe a
Just (Value -> Maybe Value) -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ [Pair] -> Value
Aeson.object
              [ "t" Text -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= String
iaTId
              , "p" Text -> String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= String
iaPId
              ]
          Nothing ->
            Maybe Value
forall a. Maybe a
Nothing

      spanData_ :: SpanData
spanData_ =
        case (QueuedSpan -> Maybe Text
serviceName QueuedSpan
span_ Maybe Text -> Maybe Text -> Maybe Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Text
serviceNameConfig_) of
          Just service :: Text
service ->
            Annotation -> SpanData -> SpanData
SpanData.merge
              (Text -> Text -> Annotation
forall a. ToJSON a => Text -> a -> Annotation
SpanData.simpleAnnotation "service" Text
service)
              (QueuedSpan -> SpanData
spanData QueuedSpan
span_)
          _ ->
            QueuedSpan -> SpanData
spanData QueuedSpan
span_
    in
    [Pair] -> Value
Aeson.object
      [ "t"     Text -> Id -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Id
tId
      , "s"     Text -> Id -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Id
spanId QueuedSpan
span_
      , "p"     Text -> Maybe Id -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Maybe Id
parentId QueuedSpan
span_
      , "n"     Text -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Text
spanName QueuedSpan
span_
      , "ts"    Text -> Int -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Int
timestamp QueuedSpan
span_
      , "d"     Text -> Int -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Int
duration QueuedSpan
span_
      , "k"     Text -> SpanKind -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> SpanKind
kind QueuedSpan
span_
      , "ec"    Text -> Int -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Int
errorCount QueuedSpan
span_
      , "crtp"  Text -> Maybe Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Maybe Text
correlationType QueuedSpan
span_
      , "crid"  Text -> Maybe Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Maybe Text
correlationId QueuedSpan
span_
      , "sy"    Text -> Maybe Bool -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Maybe Bool
synthetic QueuedSpan
span_
      , "lt"    Text -> Maybe String -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Maybe String
longTraceId
      , "tp"    Text -> Maybe Bool -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= QueuedSpan -> Maybe Bool
tpFlag QueuedSpan
span_
      , "ia"    Text -> Maybe Value -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= Maybe Value
ia
      , "data"  Text -> SpanData -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= SpanData
spanData_
      , "f"     Text -> From -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= String -> Text -> From
From String
pid_ Text
agentUuid_
      ]