packages feed

hs-opentelemetry-sdk-0.0.2.2: src/OpenTelemetry/Processor/Simple.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module OpenTelemetry.Processor.Simple
  ( SimpleProcessorConfig(..)
  , simpleProcessor
  ) where

import Control.Exception
import Control.Concurrent.Async
import Control.Concurrent.Chan.Unagi
import Control.Monad
import Data.IORef
import OpenTelemetry.Processor
import OpenTelemetry.Trace.Core (ImmutableSpan, spanTracer, tracerName)
import qualified OpenTelemetry.Exporter as Exporter
import qualified Data.HashMap.Strict as HashMap

newtype SimpleProcessorConfig = SimpleProcessorConfig
  { exporter :: Exporter.Exporter ImmutableSpan
  -- ^ The exporter where the spans are pushed.
  }

-- | This is an implementation of SpanProcessor which passes finished spans 
-- and passes the export-friendly span data representation to the configured SpanExporter, 
-- as soon as they are finished.
--
-- @since 0.0.1.0
simpleProcessor :: SimpleProcessorConfig -> IO Processor
simpleProcessor SimpleProcessorConfig{..} = do
  (inChan :: InChan (IORef ImmutableSpan), outChan :: OutChan (IORef ImmutableSpan)) <- newChan
  exportWorker <- async $ forever $ do
    -- TODO, masking vs bracket here, not sure what's the right choice
    spanRef <- readChanOnException outChan (>>= writeChan inChan)
    span_ <- readIORef spanRef
    mask_ (exporter `Exporter.exporterExport` HashMap.singleton (tracerName $ spanTracer span_) (pure span_))

  pure $ Processor
    { processorOnStart = \_ _ -> pure ()
    , processorOnEnd = writeChan inChan 
    , processorShutdown = async $ mask $ \restore -> do
        cancel exportWorker
        -- TODO handle timeouts
        restore $ do
          -- TODO, not convinced we should shut down processor here
          shutdownProcessor outChan `finally` Exporter.exporterShutdown exporter
        pure ShutdownSuccess
    , processorForceFlush = pure ()
    }

  where
    shutdownProcessor :: OutChan (IORef ImmutableSpan) -> IO ()
    shutdownProcessor outChan = do
      (Element m, _) <- tryReadChan outChan
      mSpan <- m
      case mSpan of 
        Nothing -> pure ()
        Just spanRef -> do
          span_ <- readIORef spanRef
          _ <- exporter `Exporter.exporterExport` HashMap.singleton (tracerName $ spanTracer span_) (pure span_)
          shutdownProcessor outChan