{-# LANGUAGE OverloadedStrings #-}
module OpenTelemetry.Context.Environment (
extractContextFromEnvironment,
injectContextToEnvironment,
injectCurrentContextToEnvironment,
mergeEnvironment,
normalizeKeyToEnvVar,
envTraceparent,
envTracestate,
envBaggage,
) where
import Data.Char (isAsciiLower, isAsciiUpper, isDigit, toLower, toUpper)
import qualified Data.Text as T
import OpenTelemetry.Context (Context)
import OpenTelemetry.Context.ThreadLocal (getContext)
import OpenTelemetry.Propagator (TextMap, emptyTextMap, extract, getGlobalTextMapPropagator, inject, textMapFromList, textMapToList)
import System.Environment (getEnvironment)
envTraceparent :: String
envTraceparent :: String
envTraceparent = String
"TRACEPARENT"
envTracestate :: String
envTracestate :: String
envTracestate = String
"TRACESTATE"
envBaggage :: String
envBaggage :: String
envBaggage = String
"BAGGAGE"
normalizeKeyToEnvVar :: T.Text -> String
normalizeKeyToEnvVar :: Text -> String
normalizeKeyToEnvVar Text
name =
let raw :: String
raw = (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
normalizeChar (Text -> String
T.unpack Text
name)
in case String
raw of
(Char
c : String
_) | Char -> Bool
isDigit Char
c -> Char
'_' Char -> String -> String
forall a. a -> [a] -> [a]
: (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper String
raw
String
_ -> (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper String
raw
where
normalizeChar :: Char -> Char
normalizeChar Char
c
| Char -> Bool
isAsciiUpper Char
c Bool -> Bool -> Bool
|| Char -> Bool
isAsciiLower Char
c Bool -> Bool -> Bool
|| Char -> Bool
isDigit Char
c Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_' = Char
c
| Bool
otherwise = Char
'_'
{-# INLINE normalizeKeyToEnvVar #-}
reverseNormalizeEnvVarToKey :: String -> T.Text
reverseNormalizeEnvVarToKey :: String -> Text
reverseNormalizeEnvVarToKey = String -> Text
T.pack (String -> Text) -> (String -> String) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_' then Char
'-' else Char -> Char
toLower Char
c)
{-# INLINE reverseNormalizeEnvVarToKey #-}
extractContextFromEnvironment :: IO Context
= do
propagator <- IO TextMapPropagator
getGlobalTextMapPropagator
ctx <- getContext
tm <- envToTextMap
extract propagator tm ctx
injectContextToEnvironment :: Context -> IO [(String, String)]
injectContextToEnvironment :: Context -> IO [(String, String)]
injectContextToEnvironment Context
ctx = do
propagator <- IO TextMapPropagator
getGlobalTextMapPropagator
tm <- inject propagator ctx emptyTextMap
pure (textMapToEnv tm)
injectCurrentContextToEnvironment :: IO [(String, String)]
injectCurrentContextToEnvironment :: IO [(String, String)]
injectCurrentContextToEnvironment = IO Context
forall (m :: * -> *). MonadIO m => m Context
getContext IO Context
-> (Context -> IO [(String, String)]) -> IO [(String, String)]
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Context -> IO [(String, String)]
injectContextToEnvironment
mergeEnvironment
:: [(String, String)]
-> [(String, String)]
-> [(String, String)]
mergeEnvironment :: [(String, String)] -> [(String, String)] -> [(String, String)]
mergeEnvironment [(String, String)]
traceVars [(String, String)]
baseEnv =
[(String, String)]
traceVars [(String, String)] -> [(String, String)] -> [(String, String)]
forall a. Semigroup a => a -> a -> a
<> ((String, String) -> Bool)
-> [(String, String)] -> [(String, String)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(String
k, String
_) -> String
k String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
traceKeys) [(String, String)]
baseEnv
where
traceKeys :: [String]
traceKeys = ((String, String) -> String) -> [(String, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String, String) -> String
forall a b. (a, b) -> a
fst [(String, String)]
traceVars
envToTextMap :: IO TextMap
envToTextMap :: IO TextMap
envToTextMap = do
allEnv <- IO [(String, String)]
getEnvironment
pure $ textMapFromList $ map (\(String
k, String
v) -> (String -> Text
reverseNormalizeEnvVarToKey String
k, String -> Text
T.pack String
v)) allEnv
textMapToEnv :: TextMap -> [(String, String)]
textMapToEnv :: TextMap -> [(String, String)]
textMapToEnv = ((Text, Text) -> (String, String))
-> [(Text, Text)] -> [(String, String)]
forall a b. (a -> b) -> [a] -> [b]
map (\(Text
k, Text
v) -> (Text -> String
normalizeKeyToEnvVar Text
k, Text -> String
T.unpack Text
v)) ([(Text, Text)] -> [(String, String)])
-> (TextMap -> [(Text, Text)]) -> TextMap -> [(String, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TextMap -> [(Text, Text)]
textMapToList