{-# LANGUAGE DeriveGeneric #-}
{-|
Module      : Instana.SDK.Internal.Config
Description : Internal representation of the configuration.

This internal module is not supposed to be used by client code. It's API can
change between releases, no guarantee for any kind of compatibility between
releases exists for this module.
-}
module Instana.SDK.Internal.Config
  ( FinalConfig(..)
  , mergeConfigs
  , mkFinalConfig
  , readConfigFromEnvironment
  , readConfigFromEnvironmentAndApplyDefaults
  ) where


import           Control.Applicative ((<|>))
import           Data.Maybe          (fromMaybe, isJust)
import           GHC.Generics
import           System.Environment  (lookupEnv)
import           Text.Read           (readMaybe)

import           Instana.SDK.Config  (Config)
import qualified Instana.SDK.Config  as Config


-- |Environment variable for the agent host
agentHostKey :: String
agentHostKey :: String
agentHostKey = "INSTANA_AGENT_HOST"


-- |Default agent host/IP
defaultAgentHost :: String
defaultAgentHost :: String
defaultAgentHost = "127.0.0.1"


-- |Environment variable for the agent port
agentPortKey :: String
agentPortKey :: String
agentPortKey = "INSTANA_AGENT_PORT"


-- |Default agent port
defaultAgentPort :: Int
defaultAgentPort :: Int
defaultAgentPort = 42699


-- |Environment variable for the service name override.
serviceNameKey :: String
serviceNameKey :: String
serviceNameKey = "INSTANA_SERVICE_NAME"


-- |Environment variable for the force-transmision-afeter setting
forceTransmissionAfterKey :: String
forceTransmissionAfterKey :: String
forceTransmissionAfterKey = "INSTANA_FORCE_TRANSMISSION_STARTING_AFTER"


-- |Default force-transmission-after setting
defaultForceTransmissionAfter :: Int
defaultForceTransmissionAfter :: Int
defaultForceTransmissionAfter = 1000


-- |Environment variable for the force-transmision-at setting
forceTransmissionStartingAtKey :: String
forceTransmissionStartingAtKey :: String
forceTransmissionStartingAtKey = "INSTANA_FORCE_TRANSMISSION_STARTING_AT"


-- |Default force-transmission-at setting
defaultForceTransmissionStartingAt :: Int
defaultForceTransmissionStartingAt :: Int
defaultForceTransmissionStartingAt = 500


-- |Environment variable for the max-buffered-spans setting
maxBufferedSpansKey :: String
maxBufferedSpansKey :: String
maxBufferedSpansKey = "INSTANA_MAX_BUFFERED_SPANS"


-- |Default max-buffered-spans setting
defaultMaxBufferedSpans :: Int
defaultMaxBufferedSpans :: Int
defaultMaxBufferedSpans = 1000


-- |Environment variable to disable trace correlation via W3C trace context
-- headers.
disableW3cTraceCorrelationKey :: String
disableW3cTraceCorrelationKey :: String
disableW3cTraceCorrelationKey = "INSTANA_DISABLE_W3C_TRACE_CORRELATION"


-- |Default value for disable W3C trace correlation.
defaultDisableW3cTraceCorrelation :: Bool
defaultDisableW3cTraceCorrelation :: Bool
defaultDisableW3cTraceCorrelation = Bool
False


-- |The config after evaluating and merging user provided config, environment
-- variables and default values.
data FinalConfig = FinalConfig
  { FinalConfig -> String
agentHost                   :: String
  , FinalConfig -> Int
agentPort                   :: Int
  , FinalConfig -> Maybe String
serviceName                 :: Maybe String
  , FinalConfig -> Int
forceTransmissionAfter      :: Int
  , FinalConfig -> Int
forceTransmissionStartingAt :: Int
  , FinalConfig -> Int
maxBufferedSpans            :: Int
  , FinalConfig -> Bool
disableW3cTraceCorrelation  :: Bool
  } deriving (FinalConfig -> FinalConfig -> Bool
(FinalConfig -> FinalConfig -> Bool)
-> (FinalConfig -> FinalConfig -> Bool) -> Eq FinalConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FinalConfig -> FinalConfig -> Bool
$c/= :: FinalConfig -> FinalConfig -> Bool
== :: FinalConfig -> FinalConfig -> Bool
$c== :: FinalConfig -> FinalConfig -> Bool
Eq, (forall x. FinalConfig -> Rep FinalConfig x)
-> (forall x. Rep FinalConfig x -> FinalConfig)
-> Generic FinalConfig
forall x. Rep FinalConfig x -> FinalConfig
forall x. FinalConfig -> Rep FinalConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep FinalConfig x -> FinalConfig
$cfrom :: forall x. FinalConfig -> Rep FinalConfig x
Generic, Int -> FinalConfig -> ShowS
[FinalConfig] -> ShowS
FinalConfig -> String
(Int -> FinalConfig -> ShowS)
-> (FinalConfig -> String)
-> ([FinalConfig] -> ShowS)
-> Show FinalConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FinalConfig] -> ShowS
$cshowList :: [FinalConfig] -> ShowS
show :: FinalConfig -> String
$cshow :: FinalConfig -> String
showsPrec :: Int -> FinalConfig -> ShowS
$cshowsPrec :: Int -> FinalConfig -> ShowS
Show)


-- |Creates the FinalConfig.
mkFinalConfig ::
  String
  -> Int
  -> Maybe String
  -> Int
  -> Int
  -> Int
  -> Bool
  -> FinalConfig
mkFinalConfig :: String
-> Int -> Maybe String -> Int -> Int -> Int -> Bool -> FinalConfig
mkFinalConfig
  agentHost_ :: String
agentHost_
  agentPort_ :: Int
agentPort_
  serviceName_ :: Maybe String
serviceName_
  forceTransmissionAfter_ :: Int
forceTransmissionAfter_
  forceTransmissionStartingAt_ :: Int
forceTransmissionStartingAt_
  maxBufferedSpans_ :: Int
maxBufferedSpans_
  disableW3cTraceCorrelation_ :: Bool
disableW3cTraceCorrelation_ =
  FinalConfig :: String
-> Int -> Maybe String -> Int -> Int -> Int -> Bool -> FinalConfig
FinalConfig
    { agentHost :: String
agentHost = String
agentHost_
    , agentPort :: Int
agentPort = Int
agentPort_
    , serviceName :: Maybe String
serviceName = Maybe String
serviceName_
    , forceTransmissionAfter :: Int
forceTransmissionAfter = Int
forceTransmissionAfter_
    , forceTransmissionStartingAt :: Int
forceTransmissionStartingAt = Int
forceTransmissionStartingAt_
    , maxBufferedSpans :: Int
maxBufferedSpans = Int
maxBufferedSpans_
    , disableW3cTraceCorrelation :: Bool
disableW3cTraceCorrelation = Bool
disableW3cTraceCorrelation_
    }


-- |Reads all provided config related environment variables.
readConfigFromEnvironment :: IO Config
readConfigFromEnvironment :: IO Config
readConfigFromEnvironment = do
  Maybe String
agentHostEnv <- String -> IO (Maybe String)
lookupEnv String
agentHostKey
  Maybe String
agentPortEnv <- String -> IO (Maybe String)
lookupEnv String
agentPortKey
  Maybe String
serviceNameEnv <- String -> IO (Maybe String)
lookupEnv String
serviceNameKey
  Maybe String
forceTransmissionAfterEnv <- String -> IO (Maybe String)
lookupEnv String
forceTransmissionAfterKey
  Maybe String
forceTransmissionStartingAtEnv <- String -> IO (Maybe String)
lookupEnv String
forceTransmissionStartingAtKey
  Maybe String
maxBufferedSpansEnv <- String -> IO (Maybe String)
lookupEnv String
maxBufferedSpansKey
  Maybe String
disableW3cTraceCorrelationEnv <- String -> IO (Maybe String)
lookupEnv String
disableW3cTraceCorrelationKey
  let
    -- parse numeric config values via readMaybe to Int if they were set,
    -- otherwise they remain Nothing
    agentPortParsed :: Maybe Int
agentPortParsed = Maybe String
agentPortEnv Maybe String -> (String -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe
    forceTransmissionAfterParsed :: Maybe Int
forceTransmissionAfterParsed =
      Maybe String
forceTransmissionAfterEnv Maybe String -> (String -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe
    forceTransmissionStartingAtParsed :: Maybe Int
forceTransmissionStartingAtParsed =
      Maybe String
forceTransmissionStartingAtEnv Maybe String -> (String -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe
    maxBufferedSpansParsed :: Maybe Int
maxBufferedSpansParsed = Maybe String
maxBufferedSpansEnv Maybe String -> (String -> Maybe Int) -> Maybe Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe
    disableW3cTraceCorrelationParsed :: Bool
disableW3cTraceCorrelationParsed = Maybe String -> Bool
forall a. Maybe a -> Bool
isJust Maybe String
disableW3cTraceCorrelationEnv
  Config -> IO Config
forall (m :: * -> *) a. Monad m => a -> m a
return (Config -> IO Config) -> Config -> IO Config
forall a b. (a -> b) -> a -> b
$
    Config
Config.defaultConfig
      { agentHost :: Maybe String
Config.agentHost = Maybe String
agentHostEnv
      , agentPort :: Maybe Int
Config.agentPort = Maybe Int
agentPortParsed
      , serviceName :: Maybe String
Config.serviceName = Maybe String
serviceNameEnv
      , forceTransmissionAfter :: Maybe Int
Config.forceTransmissionAfter = Maybe Int
forceTransmissionAfterParsed
      , forceTransmissionStartingAt :: Maybe Int
Config.forceTransmissionStartingAt = Maybe Int
forceTransmissionStartingAtParsed
      , maxBufferedSpans :: Maybe Int
Config.maxBufferedSpans = Maybe Int
maxBufferedSpansParsed
      , disableW3cTraceCorrelation :: Bool
Config.disableW3cTraceCorrelation = Bool
disableW3cTraceCorrelationParsed
      }


-- |Reads all provided config related environment variables and applies default
-- values for absent settings.
readConfigFromEnvironmentAndApplyDefaults :: IO FinalConfig
readConfigFromEnvironmentAndApplyDefaults :: IO FinalConfig
readConfigFromEnvironmentAndApplyDefaults = do
  Config
configFromEnv <- IO Config
readConfigFromEnvironment
  FinalConfig -> IO FinalConfig
forall (m :: * -> *) a. Monad m => a -> m a
return (FinalConfig -> IO FinalConfig) -> FinalConfig -> IO FinalConfig
forall a b. (a -> b) -> a -> b
$ Config -> FinalConfig
applyDefaults Config
configFromEnv


-- |Merges the user provided config with default values.
applyDefaults :: Config -> FinalConfig
applyDefaults :: Config -> FinalConfig
applyDefaults config :: Config
config =
  FinalConfig :: String
-> Int -> Maybe String -> Int -> Int -> Int -> Bool -> FinalConfig
FinalConfig
   { agentHost :: String
agentHost =
       String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe String
defaultAgentHost (Config -> Maybe String
Config.agentHost Config
config)
   , agentPort :: Int
agentPort =
       Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
defaultAgentPort (Config -> Maybe Int
Config.agentPort Config
config)
   , serviceName :: Maybe String
serviceName = Config -> Maybe String
Config.serviceName Config
config
   , forceTransmissionAfter :: Int
forceTransmissionAfter =
       Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe
         Int
defaultForceTransmissionAfter
         (Config -> Maybe Int
Config.forceTransmissionAfter Config
config)
   , forceTransmissionStartingAt :: Int
forceTransmissionStartingAt =
       Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe
         Int
defaultForceTransmissionStartingAt
         (Config -> Maybe Int
Config.forceTransmissionStartingAt Config
config)
   , maxBufferedSpans :: Int
maxBufferedSpans =
       Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe
         Int
defaultMaxBufferedSpans
         (Config -> Maybe Int
Config.maxBufferedSpans Config
config)
   , disableW3cTraceCorrelation :: Bool
disableW3cTraceCorrelation =
       Bool
defaultDisableW3cTraceCorrelation Bool -> Bool -> Bool
||
       (Config -> Bool
Config.disableW3cTraceCorrelation Config
config)
   }


-- |Merges two configs into a FinalConfig.
mergeConfigs :: Config -> Config -> FinalConfig
mergeConfigs :: Config -> Config -> FinalConfig
mergeConfigs userConfig :: Config
userConfig configFromEnv :: Config
configFromEnv =
  let
    merged :: Config
merged = Config
userConfig
      { agentHost :: Maybe String
Config.agentHost =
          (Config -> Maybe String
Config.agentHost Config
userConfig) Maybe String -> Maybe String -> Maybe String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe String
Config.agentHost Config
configFromEnv)
      , agentPort :: Maybe Int
Config.agentPort =
          (Config -> Maybe Int
Config.agentPort Config
userConfig) Maybe Int -> Maybe Int -> Maybe Int
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe Int
Config.agentPort Config
configFromEnv)
      , serviceName :: Maybe String
Config.serviceName =
          (Config -> Maybe String
Config.serviceName Config
userConfig) Maybe String -> Maybe String -> Maybe String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe String
Config.serviceName Config
configFromEnv)
      , forceTransmissionAfter :: Maybe Int
Config.forceTransmissionAfter =
          (Config -> Maybe Int
Config.forceTransmissionAfter Config
userConfig) Maybe Int -> Maybe Int -> Maybe Int
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe Int
Config.forceTransmissionAfter Config
configFromEnv)
      , forceTransmissionStartingAt :: Maybe Int
Config.forceTransmissionStartingAt =
          (Config -> Maybe Int
Config.forceTransmissionStartingAt Config
userConfig) Maybe Int -> Maybe Int -> Maybe Int
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe Int
Config.forceTransmissionStartingAt Config
configFromEnv)
      , maxBufferedSpans :: Maybe Int
Config.maxBufferedSpans =
          (Config -> Maybe Int
Config.maxBufferedSpans Config
userConfig) Maybe Int -> Maybe Int -> Maybe Int
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|>
          (Config -> Maybe Int
Config.maxBufferedSpans Config
configFromEnv)
      , disableW3cTraceCorrelation :: Bool
Config.disableW3cTraceCorrelation =
          (Config -> Bool
Config.disableW3cTraceCorrelation Config
userConfig) Bool -> Bool -> Bool
||
          (Config -> Bool
Config.disableW3cTraceCorrelation Config
configFromEnv)
      }
  in
    Config -> FinalConfig
applyDefaults Config
merged