packages feed

hs-opentelemetry-api-0.0.3.2: src/OpenTelemetry/Context.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  OpenTelemetry.Context
-- Copyright   :  (c) Ian Duncan, 2021
-- License     :  BSD-3
-- Description :  Carrier for execution-scoped values across API boundaries
-- Maintainer  :  Ian Duncan
-- Stability   :  experimental
-- Portability :  non-portable (GHC extensions)
--
-- The ability to correlate events across service boundaries is one of the principle concepts behind distributed tracing. To find these correlations, components in a distributed system need to be able to collect, store, and transfer metadata referred to as context.
--
-- A context will often have information identifying the current span and trace, and can contain arbitrary correlations as key-value pairs.
--
-- Propagation is the means by which context is bundled and transferred in and across services, often via HTTP headers.
--
-- Together, context and propagation represent the engine behind distributed tracing.
--
-----------------------------------------------------------------------------
module OpenTelemetry.Context
  ( Key(keyName)
  , newKey
  , Context
  , HasContext(..)
  , empty
  , lookup
  , insert
  -- , insertWith
  , adjust
  , delete
  , union
  , insertSpan
  , lookupSpan
  , removeSpan
  , insertBaggage
  , lookupBaggage
  , removeBaggage
  ) where
import Control.Monad.IO.Class
import Data.Maybe
import Data.Text (Text)
import qualified Data.Vault.Strict as V
import OpenTelemetry.Baggage (Baggage)
import OpenTelemetry.Context.Types
import OpenTelemetry.Internal.Trace.Types
import Prelude hiding (lookup)
import System.IO.Unsafe

newKey :: MonadIO m => Text -> m (Key a)
newKey n = liftIO (Key n <$> V.newKey)

class HasContext s where
  contextL :: Lens' s Context

empty :: Context
empty = Context V.empty

lookup :: Key a -> Context -> Maybe a
lookup (Key _ k) (Context v) = V.lookup k v

insert :: Key a -> a -> Context -> Context
insert (Key _ k) x (Context v) = Context $ V.insert k x v

-- insertWith 
--   :: (a -> a -> a) 
--   -- ^ new value -> old value -> result
--   -> Key a -> a -> Context -> Context
-- insertWith f (Key _ k) x (Context v) = Context $ case V.lookup k of
--   Nothing -> V.insert k x v
--   Just ox -> V.insert k (f x ox) v

adjust :: (a -> a) -> Key a -> Context -> Context
adjust f (Key _ k) (Context v) = Context $ V.adjust f k v

delete :: Key a -> Context -> Context
delete (Key _ k) (Context v) = Context $ V.delete k v

union :: Context -> Context -> Context
union (Context v1) (Context v2) = Context $ V.union v1 v2

spanKey :: Key Span
spanKey = unsafePerformIO $ newKey "span"
{-# NOINLINE spanKey #-}

lookupSpan :: Context -> Maybe Span
lookupSpan = lookup spanKey

insertSpan :: Span -> Context -> Context
insertSpan = insert spanKey

removeSpan :: Context -> Context 
removeSpan = delete spanKey

baggageKey :: Key Baggage
baggageKey = unsafePerformIO $ newKey "baggage"
{-# NOINLINE baggageKey #-}

lookupBaggage :: Context -> Maybe Baggage
lookupBaggage = lookup baggageKey

insertBaggage :: Baggage -> Context -> Context
insertBaggage b c = case lookup baggageKey c of
  Nothing -> insert baggageKey b c
  Just b' -> insert baggageKey (b <> b') c

removeBaggage :: Context -> Context
removeBaggage = delete baggageKey