packages feed

grpc-spec-1.0.0: src/Network/GRPC/Spec/TraceContext.hs

-- | Trace context
--
-- See documentation of t'TraceContext'.
module Network.GRPC.Spec.TraceContext (
    -- * Definition
    TraceContext(..)
  , TraceId(..)
  , SpanId(..)
  , TraceOptions(..)
  ) where

import Data.ByteString qualified as Strict (ByteString)
import Data.ByteString.Base16 qualified as BS.Strict.Base16
import Data.ByteString.Char8 qualified as BS.Strict.Char8
import Data.Default
import Data.String
import GHC.Generics (Generic)

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

-- | Trace context
--
-- Representation of the \"trace context\" in OpenTelemetry, corresponding
-- directly to the W3C @traceparent@ header.
--
-- References:
--
-- * <https://www.w3.org/TR/trace-context/#traceparent-header>
--   W3C spec
--
-- * <https://github.com/census-instrumentation/opencensus-specs/blob/master/encodings/BinaryEncoding.md>
--   Binary format used for the @grpc-trace-bin@ header
--
-- * <https://github.com/open-telemetry/opentelemetry-specification/issues/639>
--   Current status of the binary encoding.
--
-- Relation to Haskell OpenTelemetry implementations:
--
-- * The Haskell @opentelemetry@ package calls this a @SpanContext@, but
--    provides no binary @PropagationFormat@, and does not support
--    t'TraceOptions'.
--
--   <https://hackage.haskell.org/package/opentelemetry>
--
-- * The Haskell @hs-opentelemetry@ ecosystem defines @SpanContext@, which is
--   the combination of the W3C @traceparent@ header (our t'TraceContext') and
--   the W3C @tracestate@ header (which we do not support). It too does not
--   support the @grpc-trace-bin@ binary format.
--
--   <https://github.com/iand675/hs-opentelemetry>
--   <https://hackage.haskell.org/package/hs-opentelemetry-propagator-w3c>
data TraceContext = TraceContext {
      traceContextTraceId  :: Maybe TraceId
    , traceContextSpanId   :: Maybe SpanId
    , traceContextOptions  :: Maybe TraceOptions
    }
  deriving stock (Show, Eq, Generic)

instance Default TraceContext where
  def = TraceContext {
        traceContextTraceId = Nothing
      , traceContextSpanId  = Nothing
      , traceContextOptions = Nothing
      }

-- | Trace ID
--
-- The ID of the whole trace forest. Must be a 16-byte string.
newtype TraceId = TraceId {
      getTraceId :: Strict.ByteString
    }
  deriving stock (Eq, Generic)

-- | Span ID
--
-- ID of the caller span (parent). Must be an 8-byte string.
newtype SpanId = SpanId {
      getSpanId :: Strict.ByteString
    }
  deriving stock (Eq, Generic)

-- | Tracing options
--
-- The flags are recommendations given by the caller rather than strict rules to
-- follow for 3 reasons:
--
-- * Trust and abuse.
-- * Bug in caller
-- * Different load between caller service and callee service might force callee
--   to down sample.
data TraceOptions = TraceOptions {
      -- | Sampled
      --
      -- When set, denotes that the caller may have recorded trace data. When
      -- unset, the caller did not record trace data out-of-band.
      traceOptionsSampled :: Bool
    }
  deriving stock (Show, Eq, Generic)

{-------------------------------------------------------------------------------
  Show instances for IDs

  We follow the W3C spec and show these as base16 strings.
-------------------------------------------------------------------------------}

instance Show TraceId where
  show (TraceId tid) =
      show . BS.Strict.Char8.unpack $
        BS.Strict.Base16.encode tid

instance IsString TraceId where
  fromString str =
      case BS.Strict.Base16.decode (BS.Strict.Char8.pack str) of
        Left  err -> error err
        Right tid -> TraceId tid

instance Show SpanId where
  show (SpanId tid) =
      show . BS.Strict.Char8.unpack $
        BS.Strict.Base16.encode tid

instance IsString SpanId where
  fromString str =
      case BS.Strict.Base16.decode (BS.Strict.Char8.pack str) of
        Left  err -> error err
        Right tid -> SpanId tid