hs-opentelemetry-instrumentation-conduit 0.1.0.2 → 1.0.0.0
raw patch · 6 files changed
+141/−15 lines, 6 filesdep +hs-opentelemetry-exporter-in-memorydep +hs-opentelemetry-instrumentation-conduitdep +hs-opentelemetry-sdkdep ~hs-opentelemetry-apiPVP ok
version bump matches the API change (PVP)
Dependencies added: hs-opentelemetry-exporter-in-memory, hs-opentelemetry-instrumentation-conduit, hs-opentelemetry-sdk, hs-opentelemetry-semantic-conventions, hspec, resourcet, vector
Dependency ranges changed: hs-opentelemetry-api
API changes (from Hackage documentation)
Files
- ChangeLog.md +4/−0
- LICENSE +1/−1
- README.md +2/−0
- hs-opentelemetry-instrumentation-conduit.cabal +37/−13
- src/OpenTelemetry/Instrumentation/Conduit.hs +32/−1
- test/Spec.hs +65/−0
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for hs-opentelemetry-instrumentation-conduit +## 1.0.0.0 - 2026-05-29++- Promoted to 1.0.0.0 for the hs-opentelemetry 1.0 release.+ ## 0.1.0.2 - 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,1 +1,3 @@ # hs-opentelemetry-instrumentation-conduit++[](https://hackage.haskell.org/package/hs-opentelemetry-instrumentation-conduit)
hs-opentelemetry-instrumentation-conduit.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-conduit-version: 0.1.0.2-description: Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/conduit#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-conduit+version: 1.0.0.0+synopsis: OpenTelemetry instrumentation for Conduit streaming pipelines+description: Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/conduit#readme>+category: OpenTelemetry, Conduit, Streaming+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@@ -34,6 +36,28 @@ build-depends: base >=4.7 && <5 , conduit- , hs-opentelemetry-api ==0.3.*+ , hs-opentelemetry-api ==1.0.*+ , hs-opentelemetry-semantic-conventions >=1.40 && <2 , text+ default-language: Haskell2010++test-suite hs-opentelemetry-instrumentation-conduit-test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ other-modules:+ Paths_hs_opentelemetry_instrumentation_conduit+ hs-source-dirs:+ test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-depends:+ base >=4.7 && <5+ , conduit+ , hs-opentelemetry-api ==1.0.*+ , hs-opentelemetry-exporter-in-memory ==1.0.*+ , hs-opentelemetry-instrumentation-conduit ==1.0.*+ , hs-opentelemetry-sdk ==1.0.*+ , hspec+ , resourcet+ , text+ , vector default-language: Haskell2010
src/OpenTelemetry/Instrumentation/Conduit.hs view
@@ -1,13 +1,44 @@ {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} +{- |+Module : OpenTelemetry.Instrumentation.Conduit+Copyright : (c) Ian Duncan, 2021-2026+License : BSD-3+Description : Trace conduit pipeline stages as spans+Stability : experimental++= Overview++Wraps individual conduit pipeline stages in trace spans so you can see+how time is spent across your streaming pipeline. Use 'inSpan' to run a+sub-pipeline under a span; exceptions are recorded on the span and rethrown.++= Quick example++@+import Conduit+import Data.ByteString (ByteString)+import OpenTelemetry.Instrumentation.Conduit (inSpan)+import OpenTelemetry.Trace.Core (defaultSpanArguments)++myPipeline :: Tracer -> ConduitT ByteString Void IO ()+myPipeline tracer =+ sourceFile "input.csv"+ .| inSpan tracer "parse" defaultSpanArguments (\_ -> mapC parseRow)+ .| inSpan tracer "transform" defaultSpanArguments (\_ -> mapC transformRow)+ .| sinkFile "output.json"+@+-} module OpenTelemetry.Instrumentation.Conduit where import Conduit import Control.Exception (SomeException, throwIO) import Data.Text (Text) import GHC.Stack (HasCallStack)+import OpenTelemetry.Attributes.Key (unkey) import OpenTelemetry.Context.ThreadLocal+import qualified OpenTelemetry.SemanticConventions as SC import OpenTelemetry.Trace.Core hiding (getTracer) @@ -26,5 +57,5 @@ $ \span_ -> do catchC (f span_) $ \e -> do liftIO $ do- recordException span_ [("exception.escaped", toAttribute True)] Nothing (e :: SomeException)+ recordException span_ [(unkey SC.exception_escaped, toAttribute True)] Nothing (e :: SomeException) throwIO e
+ test/Spec.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main where++import Conduit+import Control.Exception (SomeException, throwIO, try)+import Control.Monad.IO.Class (liftIO)+import Data.IORef+import qualified Data.Vector as V+import OpenTelemetry.Exporter.InMemory.Span (inMemoryListExporter)+import OpenTelemetry.Instrumentation.Conduit (inSpan)+import OpenTelemetry.Trace.Core (Event (..))+import OpenTelemetry.Trace.Core hiding (inSpan)+import OpenTelemetry.Util (appendOnlyBoundedCollectionValues)+import System.IO.Error (userError)+import Test.Hspec+++main :: IO ()+main = hspec spec+++withTracer :: (Tracer -> IO a) -> IO ([ImmutableSpan], a)+withTracer action = do+ (processor, ref) <- inMemoryListExporter+ tp <- createTracerProvider [processor] emptyTracerProviderOptions+ let tracer = makeTracer tp "test-conduit" tracerOptions+ result <- action tracer+ _ <- shutdownTracerProvider tp Nothing+ spans <- readIORef ref+ pure (spans, result)+++firstSpan :: [ImmutableSpan] -> IO ImmutableSpan+firstSpan (s : _) = pure s+firstSpan [] = expectationFailure "No spans recorded" >> pure (error "unreachable")+++spec :: Spec+spec = describe "Conduit instrumentation" $ do+ it "creates a span wrapping a conduit pipeline" $ do+ (spans, result) <- withTracer $ \t ->+ runConduitRes $+ inSpan t "process-items" defaultSpanArguments $ \_s ->+ yieldMany [1 :: Int, 2, 3] .| sinkList+ result `shouldBe` [1, 2, 3]+ s <- firstSpan spans+ hot <- readIORef (spanHot s)+ hotName hot `shouldBe` "process-items"++ it "records exception on conduit failure" $ do+ (spans, result) <- withTracer $ \t -> do+ r <- try $+ runConduitRes $+ inSpan t "failing-conduit" defaultSpanArguments $ \_s ->+ yieldMany [1 :: Int, 2, 3] .| mapMC (\_ -> liftIO $ throwIO $ userError "conduit-boom") .| sinkList+ pure (r :: Either SomeException [Int])+ case result of+ Left _ -> pure ()+ Right _ -> expectationFailure "expected exception"+ s <- firstSpan spans+ hot <- readIORef (spanHot s)+ let events = V.toList $ appendOnlyBoundedCollectionValues $ hotEvents hot+ any (\e -> eventName e == "exception") events `shouldBe` True