{-# LANGUAGE OverloadedStrings #-}
module Control.Monad.Trace.Internal (
TraceID(..), randomTraceID,
SpanID(..), randomSpanID,
Context(..),
Name,
Span(..),
Reference(..),
Key, Value(..)
) where
import Control.Monad (replicateM)
import qualified Data.Aeson as JSON
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS.Char8
import qualified Data.ByteString.Base16 as Base16
import Data.Map.Strict (Map)
import Data.Set (Set)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Clock.POSIX (POSIXTime)
import System.Random (randomIO)
type Name = Text
type Key = Text
newtype TraceID = TraceID ByteString deriving (Eq, Ord, Show)
instance JSON.FromJSON TraceID where
parseJSON = JSON.withText "TraceID" $ \t -> case hexDecode t of
Just bs | BS.length bs == 16 -> pure $ TraceID bs
_ -> fail "invalid hex-encoded trace ID"
instance JSON.ToJSON TraceID where
toJSON (TraceID bs) = JSON.toJSON $ hexEncode bs
randomTraceID :: IO TraceID
randomTraceID = TraceID <$> randomID 16
newtype SpanID = SpanID ByteString deriving (Eq, Ord, Show)
instance JSON.FromJSON SpanID where
parseJSON = JSON.withText "SpanID" $ \t -> case hexDecode t of
Just bs | BS.length bs == 8 -> pure $ SpanID bs
_ -> fail "invalid hex-encoded span ID"
instance JSON.ToJSON SpanID where
toJSON (SpanID bs) = JSON.toJSON $ hexEncode bs
randomSpanID :: IO SpanID
randomSpanID = SpanID <$> randomID 8
data Context = Context
{ contextTraceID :: !TraceID
, contextSpanID :: !SpanID
, contextBaggages :: !(Map Key ByteString)
} deriving (Eq, Ord, Show)
data Reference
= ChildOf !SpanID
| FollowsFrom !Context
deriving (Eq, Ord, Show)
data Value
= TagValue !JSON.Value
| LogValue !JSON.Value !(Maybe POSIXTime)
data Span = Span
{ spanName :: !Name
, spanContext :: !Context
, spanReferences :: !(Set Reference)
}
randomID :: Int -> IO ByteString
randomID len = BS.pack <$> replicateM len randomIO
hexDecode :: Text-> Maybe ByteString
hexDecode t = case Base16.decode $ BS.Char8.pack $ T.unpack t of
(bs, trail) | BS.null trail -> Just bs
_ -> Nothing
hexEncode :: ByteString -> Text
hexEncode = T.pack . BS.Char8.unpack . Base16.encode