packages feed

hotel-california-0.0.6.2: src/HotelCalifornia/Tracing.hs

{-# LANGUAGE CPP #-}

module HotelCalifornia.Tracing
    ( module HotelCalifornia.Tracing
    , defaultSpanArguments
    ) where

import Control.Monad
import Data.ByteString.Char8 qualified as BS8
import Data.Text (Text)
import Data.Time
import HotelCalifornia.Tracing.TraceParent
import OpenTelemetry.Context as Context hiding (lookup)
import OpenTelemetry.Context.ThreadLocal (attachContext)
import OpenTelemetry.Trace hiding
    ( SpanKind (..)
    , SpanStatus (..)
    , addAttribute
    , addAttributes
    , createSpan
    , inSpan
    , inSpan'
    , inSpan''
    )
import OpenTelemetry.Trace qualified as Trace
import OpenTelemetry.Vendor.Honeycomb qualified as Honeycomb
import UnliftIO

-- | Initialize the global tracing provider for the application and run an action
--   (that action is generally the entry point of the application), cleaning
--   up the provider afterwards.
--
--   This also sets up an empty context (creating a new trace ID).
withGlobalTracing
    :: (MonadUnliftIO m) => (Maybe Honeycomb.HoneycombTarget -> m a) -> m a
withGlobalTracing act = do
    void $ attachContext Context.empty
    liftIO setParentSpanFromEnvironment
    withTracer $ \_ -> do
        -- note: this is not in a span since we don't have a root span yet so it
        -- would not wind up in the trace in a helpful way anyway
        mTarget <-
            Honeycomb.getOrInitializeHoneycombTargetInContext initializationTimeout
                `catch` \(e :: SomeException) -> do
                    -- we are too early in initialization to be able to use a normal logger,
                    -- but this needs to get out somehow.
                    --
                    -- honeycomb links are not load-bearing, so we let them just not come
                    -- up if the API fails.
                    liftIO . BS8.hPutStrLn stderr $
                        "error setting up Honeycomb trace links: " <> (BS8.pack $ displayException e)
                    pure Nothing

        act mTarget
  where
    initializationTimeout = secondsToNominalDiffTime 1

globalTracer :: (MonadIO m) => m Tracer
globalTracer =
    getGlobalTracerProvider >>= \tp -> pure $ makeTracer tp "hotel-california" tracerOptions

inSpan' :: (MonadUnliftIO m) => Text -> (Span -> m a) -> m a
inSpan' spanName =
    inSpanWith' spanName defaultSpanArguments

inSpanWith :: (MonadUnliftIO m) => Text -> SpanArguments -> m a -> m a
inSpanWith spanName args action =
    inSpanWith' spanName args \_ -> action

inSpanWith'
    :: (MonadUnliftIO m) => Text -> SpanArguments -> (Span -> m a) -> m a
inSpanWith' spanName args action = do
    tr <- globalTracer
    Trace.inSpan'' tr spanName args action

inSpan :: (MonadUnliftIO m) => Text -> m a -> m a
inSpan spanName =
    inSpanWith spanName defaultSpanArguments

withTracer :: (MonadUnliftIO m) => (TracerProvider -> m a) -> m a
withTracer =
    bracket (liftIO initializeGlobalTracerProvider) shutdown
  where
#if MIN_VERSION_hs_opentelemetry_api(1,0,0)
    shutdown tp = shutdownTracerProvider tp Nothing
#else
    shutdown tp = shutdownTracerProvider tp
#endif