{-# LANGUAGE OverloadedStrings #-}
module Control.Monad.Trace.Internal (
TraceID(..), encodeTraceID, decodeTraceID, randomTraceID,
SpanID(..), encodeSpanID, decodeSpanID, randomSpanID,
Context(..),
Name,
Span(..),
SamplingDecision(..), spanIsSampled, spanIsDebug,
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)
encodeTraceID :: TraceID -> Text
encodeTraceID (TraceID bs) = hexEncode bs
decodeTraceID :: Text -> Maybe TraceID
decodeTraceID txt = case hexDecode txt of
Just bs | BS.length bs == 16 -> Just $ TraceID bs
_ -> Nothing
instance JSON.FromJSON TraceID where
parseJSON = JSON.withText "TraceID" $ maybe (fail "invalid hex-encoding") pure . decodeTraceID
instance JSON.ToJSON TraceID where
toJSON = JSON.toJSON . encodeTraceID
randomTraceID :: IO TraceID
randomTraceID = TraceID <$> randomID 16
newtype SpanID = SpanID ByteString deriving (Eq, Ord, Show)
encodeSpanID :: SpanID -> Text
encodeSpanID (SpanID bs) = hexEncode bs
decodeSpanID :: Text -> Maybe SpanID
decodeSpanID txt = case hexDecode txt of
Just bs | BS.length bs == 8 -> Just $ SpanID bs
_ -> Nothing
instance JSON.FromJSON SpanID where
parseJSON = JSON.withText "SpanID" $ maybe (fail "invalid hex-encoding") pure . decodeSpanID
instance JSON.ToJSON SpanID where
toJSON = JSON.toJSON . encodeSpanID
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)
, spanSamplingDecision :: !SamplingDecision
}
data SamplingDecision
= Always
| Never
| Debug
deriving (Eq, Ord, Enum, Show)
spanIsSampled :: Span -> Bool
spanIsSampled spn = spanSamplingDecision spn /= Never
spanIsDebug :: Span -> Bool
spanIsDebug spn = spanSamplingDecision spn == Debug
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