opentracing-0.2.0: OpenTracing/Tags.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures #-}
module OpenTracing.Tags
( Tags(fromTags)
, Tag
, TagVal(..)
, setTag
, getTag
, getTagReify
-- * Standard span tags.
-- | Refer to the [OpenTracing spec](https://github.com/opentracing/specification/blob/master/semantic_conventions.md#span-tags-table)
-- for more info.
, pattern ComponentKey
, pattern DbInstanceKey
, pattern DbStatementKey
, pattern DbTypeKey
, pattern DbUserKey
, pattern ErrorKey
, pattern HttpMethodKey
, pattern HttpStatusCodeKey
, pattern HttpUrlKey
, pattern MessageBusDestinationKey
, pattern PeerAddressKey
, pattern PeerHostnameKey
, pattern PeerIPv4Key
, pattern PeerIPv6Key
, pattern PeerPortKey
, pattern PeerServiceKey
, pattern SamplingPriorityKey
, pattern SpanKindKey
, pattern Component
, pattern DbInstance
, pattern DbStatement
, pattern DbType
, pattern DbUser
, pattern Error
, pattern HttpMethod
, pattern HttpStatusCode
, pattern HttpUrl
, pattern MessageBusDestination
, pattern PeerAddress
, pattern PeerHostname
, pattern PeerIPv4
, pattern PeerIPv6
, pattern PeerPort
, pattern PeerService
, pattern SamplingPriority
, pattern SpanKind
, _Component
, _DbInstance
, _DbStatement
, _DbType
, _DbUser
, _Error
, _HttpMethod
, _HttpStatusCode
, _HttpUrl
, _MessageBusDestination
, _PeerAddress
, _PeerHostname
, _PeerIPv4
, _PeerIPv6
, _PeerPort
, _PeerService
, _SamplingPriority
, _SpanKind
, SpanKinds(..)
, spanKindLabel
)
where
import Control.Lens
import Data.Aeson (ToJSON (..))
import Data.Aeson.Encoding
import qualified Data.ByteString.Base64.Lazy as B64
import qualified Data.ByteString.Lazy as Lazy
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Int (Int64)
import Data.Monoid (First)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import qualified Data.Text.Lazy.Encoding as Lazy
import Data.Word (Word8)
import Network.HTTP.Types
import OpenTracing.Types
import Text.Read (readMaybe)
-- | Tags are structured data associated with a `OpenTracing.Span.Span`. They can give
-- a more complete picture of what a Span is doing than the operation alone. Tags
-- apply to the entire timerange of a Span. Use `OpenTracing.Log.LogField` for
-- events that refer to particular timestamp.
newtype Tags = Tags { fromTags :: HashMap Text TagVal }
deriving (Eq, Show, Semigroup, Monoid, ToJSON)
-- | A Tag is a key:value pair
type Tag = (Text, TagVal)
data TagVal
= BoolT Bool
| StringT Text
| IntT Int64
| DoubleT Double
| BinaryT Lazy.ByteString
deriving (Eq, Show)
instance ToJSON TagVal where
toJSON (BoolT x) = toJSON x
toJSON (StringT x) = toJSON x
toJSON (IntT x) = toJSON x
toJSON (DoubleT x) = toJSON x
toJSON (BinaryT x) = toJSON . Lazy.decodeUtf8 . B64.encode $ x
toEncoding (BoolT x) = toEncoding x
toEncoding (StringT x) = toEncoding x
toEncoding (IntT x) = toEncoding x
toEncoding (DoubleT x) = toEncoding x
toEncoding (BinaryT x) = toEncoding . Lazy.decodeUtf8 . B64.encode $ x
setTag :: Tag -> Tags -> Tags
setTag (k,v) = Tags . HashMap.insert k v . fromTags
getTag :: Text -> Tags -> Maybe TagVal
getTag k = HashMap.lookup k . fromTags
-- | Get a tag and attempt to convert it from a serialized format
getTagReify :: Getting (First b) Tag b -> Text -> Tags -> Maybe b
getTagReify p k ts = getTag k ts >>= preview p . (k,)
pattern ComponentKey = "component"
pattern DbInstanceKey = "db.instance"
pattern DbStatementKey = "db.statement"
pattern DbTypeKey = "db.type"
pattern DbUserKey = "db.user"
pattern ErrorKey = "error"
pattern HttpMethodKey = "http.method"
pattern HttpStatusCodeKey = "http.status_code"
pattern HttpUrlKey = "http.url"
pattern MessageBusDestinationKey = "message_bus.destination"
pattern PeerAddressKey = "peer.address"
pattern PeerHostnameKey = "peer.hostname"
pattern PeerIPv4Key = "peer.ipv4"
pattern PeerIPv6Key = "peer.ipv6"
pattern PeerPortKey = "peer.port"
pattern PeerServiceKey = "peer.service"
pattern SamplingPriorityKey = "sampling.priority"
pattern SpanKindKey = "span.kind"
_Component :: Prism' Tag Text
_Component = prism' ((ComponentKey,) . StringT) $ \case
(k, StringT v) | k == ComponentKey -> Just v
_ -> Nothing
pattern Component v <- (preview _Component -> Just v) where
Component v = review _Component v
_DbInstance :: Prism' Tag Text
_DbInstance = prism' ((DbInstanceKey,) . StringT) $ \case
(k, StringT v) | k == DbInstanceKey -> Just v
_ -> Nothing
pattern DbInstance v <- (preview _DbInstance -> Just v) where
DbInstance v = review _DbInstance v
_DbStatement :: Prism' Tag Text
_DbStatement = prism' ((DbStatementKey,) . StringT) $ \case
(k, StringT v) | k == DbStatementKey -> Just v
_ -> Nothing
pattern DbStatement v <- (preview _DbStatement -> Just v) where
DbStatement v = review _DbStatement v
_DbType :: Prism' Tag Text
_DbType = prism' ((DbTypeKey,) . StringT) $ \case
(k, StringT v) | k == DbTypeKey -> Just v
_ -> Nothing
pattern DbType v <- (preview _DbType -> Just v) where
DbType v = review _DbType v
_DbUser :: Prism' Tag Text
_DbUser = prism' ((DbUserKey,) . StringT) $ \case
(k, StringT v) | k == DbUserKey -> Just v
_ -> Nothing
pattern DbUser v <- (preview _DbUser -> Just v) where
DbUser v = review _DbUser v
_Error :: Prism' Tag Bool
_Error = prism' ((ErrorKey,) . BoolT) $ \case
(k, BoolT v) | k == ErrorKey -> Just v
_ -> Nothing
pattern Error v <- (preview _Error -> Just v) where
Error v = review _Error v
_HttpUrl :: Prism' Tag Text
_HttpUrl = prism' ((HttpUrlKey,) . StringT) $ \case
(k, StringT v) | k == HttpUrlKey -> Just v
_ -> Nothing
pattern HttpUrl v <- (preview _HttpUrl -> Just v) where
HttpUrl v = review _HttpUrl v
_MessageBusDestination :: Prism' Tag Text
_MessageBusDestination = prism' ((MessageBusDestinationKey,) . StringT) $ \case
(k, StringT v) | k == MessageBusDestinationKey -> Just v
_ -> Nothing
pattern MessageBusDestination v <- (preview _MessageBusDestination -> Just v) where
MessageBusDestination v = review _MessageBusDestination v
_PeerAddress :: Prism' Tag Text
_PeerAddress = prism' ((PeerAddressKey,) . StringT) $ \case
(k, StringT v) | k == PeerAddressKey -> Just v
_ -> Nothing
pattern PeerAddress v <- (preview _PeerAddress -> Just v) where
PeerAddress v = review _PeerAddress v
_PeerHostname :: Prism' Tag Text
_PeerHostname = prism' ((PeerHostnameKey,) . StringT) $ \case
(k, StringT v) | k == PeerHostnameKey -> Just v
_ -> Nothing
pattern PeerHostname v <- (preview _PeerHostname -> Just v) where
PeerHostname v = review _PeerHostname v
_PeerService :: Prism' Tag Text
_PeerService = prism' ((PeerServiceKey,) . StringT) $ \case
(k, StringT v) | k == PeerServiceKey -> Just v
_ -> Nothing
pattern PeerService v <- (preview _PeerService -> Just v) where
PeerService v = review _PeerService v
_HttpMethod :: Prism' Tag Method
_HttpMethod = prism' ((HttpMethodKey,) . StringT . decodeUtf8) $ \case
(k, StringT (encodeUtf8 -> x)) | k == HttpMethodKey ->
either (const Nothing) (const (Just x)) $ parseMethod x
_ -> Nothing
pattern HttpMethod v <- (preview _HttpMethod -> Just v) where
HttpMethod v = review _HttpMethod v
_HttpStatusCode :: Prism' Tag Status
_HttpStatusCode = prism' ((HttpStatusCodeKey,) . IntT . fromIntegral . statusCode) $ \case
(k, IntT x) | k == HttpStatusCodeKey -> Just . toEnum . fromIntegral $ x
_ -> Nothing
pattern HttpStatusCode v <- (preview _HttpStatusCode -> Just v) where
HttpStatusCode v = review _HttpStatusCode v
_PeerIPv4 :: Prism' Tag IPv4
_PeerIPv4 = prism' ((PeerIPv4Key,) . StringT . Text.pack . show) $ \case
(k, StringT x) | k == PeerIPv4Key -> readMaybe (Text.unpack x)
_ -> Nothing
pattern PeerIPv4 v <- (preview _PeerIPv4 -> Just v) where
PeerIPv4 v = review _PeerIPv4 v
_PeerIPv6 :: Prism' Tag IPv6
_PeerIPv6 = prism' ((PeerIPv6Key,) . StringT . Text.pack . show) $ \case
(k, StringT x) | k == PeerIPv6Key -> readMaybe (Text.unpack x)
_ -> Nothing
pattern PeerIPv6 v <- (preview _PeerIPv6 -> Just v) where
PeerIPv6 v = review _PeerIPv6 v
_PeerPort :: Prism' Tag Port
_PeerPort = prism' ((PeerPortKey,) . IntT . fromIntegral . fromPort) $ \case
(k, IntT x) | k == PeerPortKey -> Just . toEnum . fromIntegral $ x
_ -> Nothing
pattern PeerPort v <- (preview _PeerPort -> Just v) where
PeerPort v = review _PeerPort v
_SamplingPriority :: Prism' Tag Word8
_SamplingPriority = prism' ((SamplingPriorityKey,) . IntT . fromIntegral) $ \case
(k, IntT x) | k == SamplingPriorityKey -> Just . fromIntegral $ x
_ -> Nothing
pattern SamplingPriority v <- (preview _SamplingPriority -> Just v) where
SamplingPriority v = review _SamplingPriority v
_SpanKind :: Prism' Tag SpanKinds
_SpanKind = prism' ((SpanKindKey,) . StringT . spanKindLabel) $ \case
(k, StringT x) | k == SpanKindKey -> fromSpanKindLabel x
_ -> Nothing
pattern SpanKind v <- (preview _SpanKind -> Just v) where
SpanKind = review _SpanKind
data SpanKinds
= RPCClient
| RPCServer
| Producer
| Consumer
deriving (Eq, Show, Ord)
spanKindLabel :: SpanKinds -> Text
spanKindLabel RPCClient = "CLIENT"
spanKindLabel RPCServer = "SERVER"
spanKindLabel Producer = "PRODUCER"
spanKindLabel Consumer = "CONSUMER"
fromSpanKindLabel :: Text -> Maybe SpanKinds
fromSpanKindLabel "CLIENT" = Just RPCClient
fromSpanKindLabel "SERVER" = Just RPCServer
fromSpanKindLabel "PRODUCER" = Just Producer
fromSpanKindLabel "CONSUMER" = Just Consumer
fromSpanKindLabel _ = Nothing
instance ToJSON SpanKinds where
toJSON = toJSON . spanKindLabel
toEncoding = text . spanKindLabel