{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.IORef
import qualified OpenTelemetry.Context as Context
import OpenTelemetry.Exporter.InMemory.Span (inMemoryChannelExporter, inMemoryListExporter, readChan)
import OpenTelemetry.Trace.Core
import Test.Hspec
main :: IO ()
main = hspec spec
spec :: Spec
spec =
-- In-memory span exporter / processor (implementation-specific test helper)
-- https://opentelemetry.io/docs/specs/otel/trace/sdk/#span-processor
do
describe "inMemoryListExporter" $ do
it "starts with empty list" $ do
(_, ref) <- inMemoryListExporter
spans <- readIORef ref
length spans `shouldBe` 0
-- Ended spans exported to processor (SimpleSpanProcessor behavior)
-- https://opentelemetry.io/docs/specs/otel/trace/sdk/#simplespanprocessor
it "captures ended spans" $ do
(processor, ref) <- inMemoryListExporter
tp <- createTracerProvider [processor] emptyTracerProviderOptions
let t = makeTracer tp "test" tracerOptions
s <- createSpan t Context.empty "test-span" defaultSpanArguments
endSpan s Nothing
_ <- shutdownTracerProvider tp Nothing
spans <- readIORef ref
length spans `shouldBe` 1
case spans of
(s : _) -> do
hot <- readIORef (spanHot s)
hotName hot `shouldBe` "test-span"
[] -> expectationFailure "no spans"
it "captures multiple spans in order of completion" $ do
(processor, ref) <- inMemoryListExporter
tp <- createTracerProvider [processor] emptyTracerProviderOptions
let t = makeTracer tp "test" tracerOptions
s1 <- createSpan t Context.empty "first" defaultSpanArguments
s2 <- createSpan t Context.empty "second" defaultSpanArguments
endSpan s1 Nothing
endSpan s2 Nothing
_ <- shutdownTracerProvider tp Nothing
spans <- readIORef ref
length spans `shouldBe` 2
it "does not capture spans that have not ended" $ do
(processor, ref) <- inMemoryListExporter
tp <- createTracerProvider [processor] emptyTracerProviderOptions
let t = makeTracer tp "test" tracerOptions
_ <- createSpan t Context.empty "unfinished" defaultSpanArguments
_ <- shutdownTracerProvider tp Nothing
spans <- readIORef ref
length spans `shouldBe` 0
describe "inMemoryChannelExporter" $ do
it "creates a processor and channel" $ do
(_, _chan) <- inMemoryChannelExporter
pure () :: IO ()
it "captures ended spans via channel" $ do
(processor, chan) <- inMemoryChannelExporter
tp <- createTracerProvider [processor] emptyTracerProviderOptions
let t = makeTracer tp "test" tracerOptions
s <- createSpan t Context.empty "chan-span" defaultSpanArguments
endSpan s Nothing
_ <- shutdownTracerProvider tp Nothing
element <- readChan chan
hot <- readIORef (spanHot element)
hotName hot `shouldBe` "chan-span"