{-# LANGUAGE UndecidableInstances #-}
module Main where
import Servant.Tracing ( getInstructions, WithTracing)
import Tracing.Core (recordSpan,TracingInstructions(..), SpanRelationTag(..), Tracer(..), MonadTracer(..), SpanId(..),
TraceId(..), debugPrintSpan)
import Tracing.Zipkin (publishZipkin)
import Control.Concurrent (threadDelay, forkIO)
import Control.Monad (forever, mapM_)
import Control.Monad.Trans (MonadIO, liftIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Reader (ReaderT(..), ask, MonadReader)
import Data.IORef (IORef, newIORef, atomicModifyIORef')
import Data.Maybe (maybe)
import Data.Proxy (Proxy(..))
import Data.Foldable (toList)
import Data.ByteString.Char8 as BS
import Servant
import Servant.Server
import System.Environment (lookupEnv, getEnv)
import qualified Data.Text as T
import Network.Wai.Handler.Warp (run)
import Network.HTTP.Client (Manager, newManager, defaultManagerSettings, responseStatus, responseBody)
main :: IO ()
main = do
debug <- maybe False (== "TRUE") <$> lookupEnv "TRACE_DEBUG"
destinationPath <- getEnv "TRACING_ENDPOINT"
svcName <- T.pack <$> getEnv "TRACING_SERVICE"
httpManager <- newManager defaultManagerSettings
cell <- newIORef []
let tracer = Tracer cell svcName
forkIO $ publishLoop destinationPath httpManager tracer
run 8080 . serve (Proxy :: Proxy ExampleAPI) $ server tracer
publishLoop ::
String
-> Manager
-> Tracer
-> IO ()
publishLoop destination manager (Tracer {spanBuffer}) = forever $ do
threadDelay 5000000
buffer <- atomicModifyIORef' spanBuffer (\b -> ([], b))
mResp <- publishZipkin destination manager $ toList buffer
case mResp of
Nothing -> pure ()
Just resp -> do
print $ "Ran Loop " ++ (show $ responseStatus resp) ++ " " ++ (show . fmap debugPrintSpan $ toList buffer)
print $ " " ++ (T.unpack $ responseBody resp)
--
-- API Definition
--
type ExampleAPI =
WithTracing :> MyAPI
type MyAPI =
Header "Auth" T.Text :>
(
"fast" :> Get '[JSON] Int
:<|>
"slow" :> Get '[JSON] T.Text
)
--
-- Server logic
--
server :: Tracer -> Server (WithTracing :> MyAPI)
server tracer inst auth =
runFast
:<|>
runSlow
where
loadCtx = do
instructions <- getInstructions True inst
currSpan <- liftIO $ newIORef (spanId instructions)
pure Ctx {
tracer,
currSpan,
instructions
}
runFast = do
ctx <- loadCtx
runStack ctx $
recordSpan (const Child <$> inst) [] "Run Fast" . liftIO $
threadDelay 1000000 *> pure 42
runSlow = do
ctx <- loadCtx
runStack ctx $
recordSpan (const Child <$> inst) [] "Run Slow" $ do
liftIO $ threadDelay 500000
let action = liftIO $ threadDelay 1500000 *> pure "Boo"
recordSpan (Just Child) [] "Slow Child" action
runStack :: Ctx -> ReaderT Ctx Handler a -> Handler a
runStack ctx action = runReaderT action ctx
data Ctx = Ctx {
tracer :: Tracer,
currSpan :: IORef SpanId,
instructions :: TracingInstructions
}
instance (Monad m, MonadBaseControl IO m, MonadIO m, MonadReader Ctx m) => MonadTracer m where
getTracer = tracer <$> ask
currentTrace = (traceId . instructions) <$> ask
currentSpan = currSpan <$> ask
isDebug = (debug . instructions) <$> ask