packages feed

opentracing-0.1.0.0: OpenTracing/Standard.hs

{-|
Module: OpenTracing.Standard

Standard implementations of `OpenTracing.Tracer` fields.
-}

{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns        #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE StrictData            #-}
{-# LANGUAGE TemplateHaskell       #-}

module OpenTracing.Standard
    ( StdEnv
    , newStdEnv
    , envTraceID128bit
    , envSampler

    , stdTracer
    , stdReporter
    )
where

import Control.Concurrent (MVar, newMVar, withMVar)
import Control.Lens                 hiding (Context, (.=))
import Control.Monad.Reader
import Data.Monoid
import Data.Word
import OpenTracing.Reporting.Stdio (stdoutReporter)
import OpenTracing.Sampling         (Sampler (runSampler))
import OpenTracing.Span
import OpenTracing.Types
import Prelude                      hiding (putStrLn)
import System.Random.MWC

-- | A standard environment for generating trace and span IDs.
data StdEnv = StdEnv
    { envPRNGRef        :: MVar GenIO
    , _envSampler       :: Sampler
    , _envTraceID128bit :: Bool
    }

newStdEnv :: MonadIO m => Sampler -> m StdEnv
newStdEnv samp = do
    prng <- liftIO createSystemRandom
    prngRef <- liftIO $ newMVar prng
    return StdEnv { envPRNGRef = prngRef, _envSampler = samp, _envTraceID128bit = True }

makeLenses ''StdEnv

-- | A standard implementation of `OpenTracing.Tracer.tracerStart`.
stdTracer :: MonadIO m => StdEnv -> SpanOpts -> m Span
stdTracer r = flip runReaderT r . start

-- | A implementation of `OpenTracing.Tracer.tracerReport` that logs spans to stdout.
stdReporter :: MonadIO m => FinishedSpan -> m ()
stdReporter = stdoutReporter

--------------------------------------------------------------------------------
-- Internal

start :: (MonadIO m, MonadReader StdEnv m) => SpanOpts -> m Span
start so = do
    ctx <- do
        p <- findParent <$> liftIO (freezeRefs (view spanOptRefs so))
        case p of
            Nothing -> freshContext so
            Just p' -> fromParent   (refCtx p')
    newSpan ctx
            (view spanOptOperation so)
            (view spanOptRefs so)
            (view spanOptTags so)

newTraceID :: (MonadIO m, MonadReader StdEnv m) => m TraceID
newTraceID = do
    StdEnv{..} <- ask
    liftIO $ withMVar envPRNGRef $ \prng -> do
      hi <- if _envTraceID128bit then
                Just <$> liftIO (uniform prng)
            else
                pure Nothing
      lo <- liftIO $ uniform prng
      return TraceID { traceIdHi = hi, traceIdLo = lo }

newSpanID :: (MonadIO m, MonadReader StdEnv m) => m Word64
newSpanID = do
  prngRef <- asks envPRNGRef
  liftIO $ withMVar prngRef $ \prng -> do
    uniform prng

freshContext
    :: ( MonadIO            m
       , MonadReader StdEnv m
       )
    => SpanOpts
    -> m SpanContext
freshContext so = do
    trid <- newTraceID
    spid <- newSpanID
    smpl <- view envSampler

    sampled' <- case view spanOptSampled so of
        Nothing -> view _IsSampled
               <$> runSampler smpl trid (view spanOptOperation so)
        Just s  -> pure s

    return SpanContext
        { ctxTraceID      = trid
        , ctxSpanID       = spid
        , ctxParentSpanID = Nothing
        , _ctxSampled     = sampled'
        , _ctxBaggage     = mempty
        }

fromParent
    :: ( MonadIO            m
       , MonadReader StdEnv m
       )
    => SpanContext
    -> m SpanContext
fromParent p = do
    spid <- newSpanID
    return SpanContext
        { ctxTraceID      = ctxTraceID p
        , ctxSpanID       = spid
        , ctxParentSpanID = Just (ctxSpanID p)
        , _ctxSampled     = view ctxSampled p
        , _ctxBaggage     = view ctxBaggage p
        }