packages feed

apiary-helics-1.1.2: src/Web/Apiary/Helics.hs

{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

module Web.Apiary.Helics
    ( Helics
    -- * initializer
    , HelicsConfig(..)
    , initHelics
    -- * action
    -- ** raw
    , transactionId
    -- ** metric
    , recordMetric
    -- ** transaction
    , addAttribute
    , setError
    , noticeError
    , clearError
    -- ** segment
    , genericSegment
    , datastoreSegment
    , externalSegment
    -- * reexports
    , H.autoScope
    , H.rootSegment
    , H.TransactionId
    , H.TransactionError
    , H.SegmentId
    , H.Operation(..)
    , H.DatastoreSegment(..)
    ) where

import Control.Concurrent
import Control.Applicative
import Control.Monad.Apiary.Action
import Control.Monad.IO.Class
import Control.Monad.Trans.Control

import Network.Wai
import qualified Network.Helics as H
import qualified Network.Helics.Wai.Safe as Safe

import Data.Apiary.Compat
import Data.Apiary.Extension
import Data.Default.Class
import qualified Data.Vault.Lazy as V
import qualified Data.ByteString as S

data Helics = Helics (V.Key H.TransactionId) ThreadId HelicsConfig

instance Extension Helics where
    extMiddleware (Helics k _ cfg) =
        if useDummyMiddleware cfg
        then Safe.dummyHelics k
        else Safe.helics k (toHelicsMiddlewareConfig cfg)

data HelicsConfig = HelicsConfig
    { licenseKey         :: S.ByteString
    , appName            :: S.ByteString
    , language           :: S.ByteString
    , languageVersion    :: S.ByteString
    , statusCallback     :: Maybe (H.StatusCode -> IO ())

    , useDummyMiddleware :: Bool
    , transactionName    :: Request -> S.ByteString

    , samplerFrequency   :: Int
    }

instance Default HelicsConfig where
    def = HelicsConfig
        { licenseKey         = H.licenseKey def
        , appName            = H.appName def
        , language           = H.language def
        , languageVersion    = H.languageVersion def
        , statusCallback     = H.statusCallback def
        , transactionName    = rawPathInfo
        , useDummyMiddleware = False
        , samplerFrequency   = 20
        }

toHelicsConfig :: HelicsConfig -> H.HelicsConfig
toHelicsConfig c = def
    { H.licenseKey      = licenseKey c
    , H.appName         = appName c
    , H.language        = language c
    , H.languageVersion = languageVersion c
    , H.statusCallback  = statusCallback c
    }

toHelicsMiddlewareConfig :: HelicsConfig -> Safe.HelicsMiddlewareConfig
toHelicsMiddlewareConfig c = def
    { Safe.transactionName = transactionName c }

initHelics :: (MonadBaseControl IO m, MonadIO m) => HelicsConfig -> Initializer' m Helics
initHelics cnf = initializerBracket' $ \m -> do
    k <- liftIO $ V.newKey
    control $ \run -> H.withHelics (toHelicsConfig cnf) $ run $ do
        tid <- liftIO $ forkIO $ H.sampler (samplerFrequency cnf)
        m (Helics k tid cnf)

recordMetric :: MonadIO m => S.ByteString -> Double
             -> ActionT exts prms m ()
recordMetric n v = liftIO $ H.recordMetric n v

transactionId :: (Has Helics exts, Monad m)
              => ActionT exts prms m H.TransactionId
transactionId = do
    Helics key _ _ <- getExt Proxy
    maybe (error "apiary-helics: vault value not found.") id .
        V.lookup key . vault <$> getRequest

addAttribute :: (MonadIO m, Has Helics exts)
             => S.ByteString -> S.ByteString -> ActionT exts prms m ()
addAttribute key val = do
    tid <- transactionId
    liftIO $ H.addAttribute key val tid

genericSegment :: (Has Helics exts, MonadBaseControl IO m)
               => H.SegmentId  -- ^ parent segment id
               -> S.ByteString -- ^ name of represent segment
               -> ActionT exts prms m a
               -> ActionT exts prms m a
genericSegment sid name act = do
    tid <- transactionId
    control $ \run -> H.genericSegment sid name (run act) tid

datastoreSegment :: (Has Helics exts, MonadBaseControl IO m)
                 => H.SegmentId  -- ^ parent segment id
                 -> H.DatastoreSegment
                 -> ActionT exts prms m a
                 -> ActionT exts prms m a
datastoreSegment sid ds act = do
    tid <- transactionId
    control $ \run -> H.datastoreSegment sid ds (run act) tid

externalSegment :: (Has Helics exts, MonadBaseControl IO m)
                => H.SegmentId  -- ^ parent segment id
                -> S.ByteString -- ^ host of segment
                -> S.ByteString -- ^ name of segment
                -> ActionT exts prms m a
                -> ActionT exts prms m a
externalSegment sid host name act = do
    tid <- transactionId
    control $ \run -> H.externalSegment sid host name (run act) tid

setError :: (Has Helics exts, MonadIO m)
         => Maybe H.TransactionError -> ActionT exts prms m ()
setError err = do
    tid <- transactionId
    liftIO $ H.setError err tid

noticeError :: (Has Helics exts, MonadIO m)
            => H.TransactionError -> ActionT exts prms m ()
noticeError err = do
    tid <- transactionId
    liftIO $ H.noticeError err tid

clearError :: (Has Helics exts, MonadIO m) => ActionT exts prms m ()
clearError = do
    tid <- transactionId
    liftIO $ H.clearError tid