packages feed

hs-opentelemetry-instrumentation-hspec 0.0.1.3 → 1.0.0.0

raw patch · 6 files changed

+159/−30 lines, 6 filesdep +hs-opentelemetry-exporter-in-memorydep +hs-opentelemetry-instrumentation-hspecdep +hs-opentelemetry-sdkdep ~hs-opentelemetry-apidep ~hspec-corePVP ok

version bump matches the API change (PVP)

Dependencies added: hs-opentelemetry-exporter-in-memory, hs-opentelemetry-instrumentation-hspec, hs-opentelemetry-sdk, hspec

Dependency ranges changed: hs-opentelemetry-api, hspec-core

API changes (from Hackage documentation)

Files

ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for hs-opentelemetry-instrumentation-hspec +## 1.0.0.0 - 2026-05-29++- Promoted to 1.0.0.0 for the hs-opentelemetry 1.0 release.+ ## 0.0.1.3  - Relax `hs-opentelemetry-api` bounds to support 0.3.x
LICENSE view
@@ -1,4 +1,4 @@-Copyright Ian Duncan (c) 2021+Copyright Ian Duncan (c) 2021-2026  All rights reserved. 
README.md view
@@ -1,9 +1,23 @@ # hs-opentelemetry-instrumentation-hspec -Instrumentation for Hspec for OpenTelemetry. This creates a span for each test-case inside a test suite.+[![hs-opentelemetry-instrumentation-hspec](https://img.shields.io/hackage/v/hs-opentelemetry-instrumentation-hspec?style=flat-square&logo=haskell&label=hs-opentelemetry-instrumentation-hspec&labelColor=5D4F85)](https://hackage.haskell.org/package/hs-opentelemetry-instrumentation-hspec) +OpenTelemetry instrumentation for the [Hspec] test framework; it creates one span per test case inside the instrumented `Spec` tree.++[Hspec]: https://hackage.haskell.org/package/hspec+ ## Usage -See the example in [examples/hspec](../../examples/hspec) for an instrumented-test suite.+```haskell+do+  provider <- getGlobalTracerProvider+  let tracer = OpenTelemetry.Trace.makeTracer provider "my-test-suite" OpenTelemetry.Trace.tracerOptions+  context <- OpenTelemetry.Context.ThreadLocal.getContext++  hspec $ instrumentSpec tracer context $ do+    describe "Spec" do+      it "is instrumented with OpenTelemetry" do+        True `shouldBe` True+```++See [examples/hspec](../../examples/hspec) for an instrumented test suite.
hs-opentelemetry-instrumentation-hspec.cabal view
@@ -1,20 +1,22 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack -name:               hs-opentelemetry-instrumentation-hspec-version:            0.0.1.3-description:        Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/hspec#readme>-homepage:           https://github.com/iand675/hs-opentelemetry#readme-bug-reports:        https://github.com/iand675/hs-opentelemetry/issues-author:             Ian Duncan, Jade Lovelace-maintainer:         ian@iankduncan.com-copyright:          2024 Ian Duncan, Mercury Technologies-license:            BSD3-license-file:       LICENSE-build-type:         Simple+name:           hs-opentelemetry-instrumentation-hspec+version:        1.0.0.0+synopsis:       OpenTelemetry instrumentation for Hspec test suites+description:    Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/hspec#readme>+category:       OpenTelemetry, Testing+homepage:       https://github.com/iand675/hs-opentelemetry#readme+bug-reports:    https://github.com/iand675/hs-opentelemetry/issues+author:         Ian Duncan, Jade Lovelace+maintainer:     ian@iankduncan.com+copyright:      2024 Ian Duncan, Mercury Technologies+license:        BSD3+license-file:   LICENSE+build-type:     Simple extra-source-files:     README.md     ChangeLog.md@@ -32,8 +34,27 @@       src   build-depends:       base >=4.7 && <5-    , hs-opentelemetry-api ==0.3.*+    , hs-opentelemetry-api ==1.0.*     , hspec-core >=2.9.4     , mtl+    , text+  default-language: Haskell2010++test-suite hs-opentelemetry-hspec-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_hs_opentelemetry_instrumentation_hspec+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , hs-opentelemetry-api ==1.0.*+    , hs-opentelemetry-exporter-in-memory ==1.0.*+    , hs-opentelemetry-instrumentation-hspec ==1.0.*+    , hs-opentelemetry-sdk ==1.0.*+    , hspec+    , hspec-core     , text   default-language: Haskell2010
src/OpenTelemetry/Instrumentation/Hspec.hs view
@@ -1,24 +1,56 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} --- | Instrumentation for Hspec test suites+{- |+Module      : OpenTelemetry.Instrumentation.Hspec+Copyright   : (c) Ian Duncan, 2021-2026+License     : BSD-3+Description : Trace Hspec test suites as spans+Stability   : experimental++= Overview++Adds tracing around Hspec examples. Useful for CI observability: see which+tests ran, how long they took, and which ones failed.++'wrapSpec' uses the global tracer provider; compose it with+'OpenTelemetry.Trace.withTracerProvider' so the provider is initialized before+@hspec@ runs. For full control over tracer and parent context, use+'instrumentSpec' instead.++= Quick example++@+import Test.Hspec+import OpenTelemetry.Instrumentation.Hspec (wrapSpec)+import OpenTelemetry.Trace (withTracerProvider)++main :: IO ()+main = withTracerProvider $ \_ -> do+  runSpec <- wrapSpec+  hspec $ runSpec mySpec+@++'OpenTelemetry.Trace.withTracerProvider' lives in @hs-opentelemetry-sdk@.++= Nested structure++'instrumentSpec' adds spans for @describe@ nesting; 'wrapSpec' produces a flat+span per @it@ (see its Haddock for rationale).+-} module OpenTelemetry.Instrumentation.Hspec (   wrapSpec,   wrapExampleInSpan,   instrumentSpec, ) where -import Control.Monad (void) import Control.Monad.IO.Class-import Control.Monad.Reader import qualified Data.List as List-import Data.Text (Text) import qualified Data.Text as T-import OpenTelemetry.Attributes (Attributes) import OpenTelemetry.Context-import OpenTelemetry.Context.ThreadLocal (adjustContext, attachContext, getContext)+import OpenTelemetry.Context.ThreadLocal (attachContext, getContext) import OpenTelemetry.Trace.Core-import Test.Hspec.Core.Spec (ActionWith, Item (..), Spec, SpecWith, Tree (..), mapSpecForest, mapSpecItem_)+import Test.Hspec.Core.Spec (Item (..), SpecWith, Tree (..), mapSpecForest, mapSpecItem_)   {- | Creates a wrapper function that you can pass a spec into.@@ -35,9 +67,9 @@   let tracer = makeTracer tp $detectInstrumentationLibrary tracerOptions   context <- getContext -  -- FIXME: this kind of just dumps everything flat into one span per `it`. We-  -- could possibly do better, e.g. finding the `describe`s and making them into-  -- spans but I am not sure how that would be achieved.+  -- Span structure is flat: one span per @it@ item. Nesting spans under+  -- @describe@ blocks would require access to hspec's internal spec tree+  -- before execution, which isn't exposed by the hspec public API.   pure $ \spec -> mapSpecItem_ (wrapExampleInSpan tracer context) spec  @@ -50,7 +82,7 @@     { itemExample = \params aroundAction pcb -> do         let aroundAction' a = do               -- we need to reattach the context, since we are on a forked thread-              void $ attachContext traceContext+              _ <- attachContext traceContext               inSpan tp (T.pack req) defaultSpanArguments (aroundAction a)          ex params aroundAction' pcb@@ -75,7 +107,7 @@             { itemExample = \params aroundAction pcb -> do                 let aroundAction' a = do                       -- we need to reattach the context, since we are on a forked thread-                      void $ attachContext traceContext+                      _ <- attachContext traceContext                       addSpans spans $ inSpan tracer (T.pack (itemRequirement item)) defaultSpanArguments (aroundAction a)                  itemExample item params aroundAction' pcb
+ test/Spec.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE OverloadedStrings #-}++module Main where++import Data.IORef+import OpenTelemetry.Context.ThreadLocal (getContext)+import OpenTelemetry.Exporter.InMemory.Span (inMemoryListExporter)+import OpenTelemetry.Instrumentation.Hspec (instrumentSpec)+import OpenTelemetry.Trace.Core+import Test.Hspec+import Test.Hspec.Core.Runner (hspecResult)+++main :: IO ()+main = hspec spec+++spec :: Spec+spec = describe "Hspec instrumentation" $ do+  it "instrumentSpec creates spans for each test item" $ do+    (processor, ref) <- inMemoryListExporter+    tp <- createTracerProvider [processor] emptyTracerProviderOptions+    setGlobalTracerProvider tp+    let tracer = makeTracer tp "test" tracerOptions+    ctx <- getContext++    let innerSpec = instrumentSpec tracer ctx $ do+          it "alpha" $ True `shouldBe` True+          it "beta" $ True `shouldBe` True++    _summary <- hspecResult innerSpec++    _ <- shutdownTracerProvider tp Nothing+    spans <- readIORef ref+    names <- traverse (\s -> hotName <$> readIORef (spanHot s)) spans+    names `shouldContain` ["alpha"]+    names `shouldContain` ["beta"]++  it "instrumentSpec nests describe groups as spans" $ do+    (processor, ref) <- inMemoryListExporter+    tp <- createTracerProvider [processor] emptyTracerProviderOptions+    setGlobalTracerProvider tp+    let tracer = makeTracer tp "test-nested" tracerOptions+    ctx <- getContext++    let innerSpec =+          instrumentSpec tracer ctx $+            describe "outer group" $+              it "nested test" $+                True `shouldBe` True++    _summary <- hspecResult innerSpec++    _ <- shutdownTracerProvider tp Nothing+    spans <- readIORef ref+    names <- traverse (\s -> hotName <$> readIORef (spanHot s)) spans+    names `shouldContain` ["nested test"]+    names `shouldContain` ["outer group"]