{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} module OpenTelemetry.TraceSpec where import Control.Monad import Data.Int import Data.IORef import Data.Text (Text) import qualified OpenTelemetry.Context as Context import OpenTelemetry.Trace import OpenTelemetry.Trace.Core import OpenTelemetry.Trace.Id import OpenTelemetry.Trace.Id.Generator import OpenTelemetry.Trace.Id.Generator.Default import qualified OpenTelemetry.Trace.TraceState as TraceState import Test.Hspec import System.Clock asIO :: IO a -> IO a asIO = id spec :: Spec spec = describe "Trace" $ do describe "TracerProvider" $ do specify "Create TracerProvider" $ do void (createTracerProvider [] (emptyTracerProviderOptions :: TracerProviderOptions) :: IO TracerProvider) specify "Get a Tracer" $ asIO $ do p <- getGlobalTracerProvider void $ getTracer p "woo" tracerOptions specify "Get a Tracer with schema_url" $ asIO $ do p <- getGlobalTracerProvider void $ getTracer p "woo" (tracerOptions { tracerSchema = Just "https://woo.com" }) specify "Safe for concurrent calls" pending specify "Shutdown" pending specify "ForceFlush" pending describe "Trace / Context interaction" $ do specify "Set active span, Get active span" $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments spanContext1 <- spanContext <$> unsafeReadSpan s let ctxt = Context.insertSpan s mempty let Just s' = Context.lookupSpan ctxt spanContext2 <- spanContext <$> unsafeReadSpan s' spanContext1 `shouldBe` spanContext2 describe "Tracer" $ do specify "Create a new span" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions void $ createSpan t Context.empty "create_root_span" defaultSpanArguments specify "Get active new span" pending specify "Mark Span active" pending specify "Safe for concurrent calls" pending describe "SpanContext" $ do specify "IsValid" $ do t <- newTraceId defaultIdGenerator s <- newSpanId defaultIdGenerator let (Right goodSpan) = bytesToSpanId "\1\0\0\0\0\0\0\1" (Right goodTrace) = bytesToTraceId "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1" (Right badSpan) = bytesToSpanId "\0\0\0\0\0\0\0\0" (Right badTrace) = bytesToTraceId "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" validSpan = SpanContext { traceFlags = defaultTraceFlags , isRemote = False , traceState = TraceState.empty , spanId = goodSpan , traceId = goodTrace } validSpan `shouldSatisfy` isValid (validSpan { spanId = badSpan, traceId = badTrace }) `shouldSatisfy` (not . isValid) specify "IsRemote" pending specify "Conforms to the W3C TraceContext spec" pending describe "Span" $ do specify "Create root span" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions void $ createSpan t Context.empty "create_root_span" defaultSpanArguments specify "Create with default parent (active span)" pending specify "Create with parent from Context" pending -- specify "No explict parent from Span/SpanContext allowed" pending specify "Processor.OnStart receives parent Context" pending specify "UpdateName" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments updateName s "renamed_span" specify "User-defined start timestamp" pending specify "End" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments endSpan s Nothing specify "End with timestamp" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions ts <- getTimestamp s <- createSpan t Context.empty "create_root_span" defaultSpanArguments endSpan s (Just ts) specify "IsRecording" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions ts <- getTime Realtime s <- createSpan t Context.empty "create_root_span" defaultSpanArguments recording <- isRecording s recording `shouldBe` True specify "IsRecording becomes false after End" $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions ts <- getTime Realtime s <- createSpan t Context.empty "create_root_span" defaultSpanArguments endSpan s Nothing recording <- isRecording s recording `shouldBe` False specify "Set status with StatusCode (Unset, Ok, Error)" $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions ts <- getTime Realtime s <- createSpan t Context.empty "create_root_span" defaultSpanArguments setStatus s $ Error "woo" do i <- unsafeReadSpan s spanStatus i `shouldBe` Error "woo" setStatus s $ Ok do i <- unsafeReadSpan s spanStatus i `shouldBe` Ok setStatus s $ Error "woo" do i <- unsafeReadSpan s spanStatus i `shouldBe` Ok specify "Safe for concurrent calls" pending specify "events collection size limit" pending specify "attribute collection size limit" pending specify "links collection size limit" pending describe "Span attributes" $ do specify "SetAttribute" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "attr" (1.0 :: Double) specify "Set order preserved" pending specify "String type" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "string_type" ("" :: Text) specify "Boolean type" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "bool_type" True specify "Double floating-point type" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "attr" (1.0 :: Double) specify "Signed int64 type" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "attr" (1 :: Int64) specify "Array of primitives (homegeneous)" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "attr" [(1 :: Int64)..10] specify "Unicode support for keys and string values" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addAttribute s "🚀" ("🚀" :: Text) -- TODO actually get attributes out describe "Span events" $ do specify "AddEvent" $ asIO $ do p <- getGlobalTracerProvider t <- getTracer p "woo" tracerOptions s <- createSpan t Context.empty "create_root_span" defaultSpanArguments addEvent s $ NewEvent { newEventName = "EVENT" , newEventAttributes = [] , newEventTimestamp = Nothing } specify "Add order preserved" pending specify "Safe for concurrent calls" pending describe "Span exceptions" $ do specify "RecordException" pending specify "RecordException with extra parameters" pending describe "Sampling" $ do specify "Allow samplers to modify tracestate" pending specify "ShouldSample gets full parent Context" pending specify "ShouldSample gets InstrumentationLibrary" pending specify "New Span ID created also for non-recording spans" pending specify "IdGenerators" pending specify "SpanLimits" pending specify "Built-in Processors implement ForceFlush spec" pending specify "Attribute Limits" pending