shibuya-core 0.5.0.0 → 0.6.0.0
raw patch · 7 files changed
+65/−46 lines, 7 files
Files
- CHANGELOG.md +22/−0
- shibuya-core.cabal +5/−6
- src/Shibuya/Core/Types.hs +1/−1
- src/Shibuya/Telemetry/Effect.hs +3/−3
- src/Shibuya/Telemetry/Semantic.hs +5/−5
- test/Shibuya/Telemetry/PropagationSpec.hs +15/−22
- test/Shibuya/Telemetry/SemanticSpec.hs +14/−9
CHANGELOG.md view
@@ -1,5 +1,27 @@ # Changelog +## 0.6.0.0 — 2026-05-31++### Breaking Changes++- OpenTelemetry messaging spans now emit `messaging.operation.type =+ "process"` instead of the deprecated `messaging.operation = "process"`+ wire key. The Haskell constant `attrMessagingOperation` keeps the same+ name and now resolves to the current semantic-conventions key, so source+ imports continue to compile. Dashboards, alerts, and trace queries that+ filter on `messaging.operation` must be updated to+ `messaging.operation.type`.++### Other Changes++- Upgrade OpenTelemetry dependencies to the 1.0 ecosystem:+ `hs-opentelemetry-api ^>= 1.0`,+ `hs-opentelemetry-propagator-w3c ^>= 1.0`, and test-only+ `hs-opentelemetry-exporter-in-memory ^>= 1.0`.+- Move `hs-opentelemetry-semantic-conventions` to the latest Haskell+ generated package available with the 1.0 release, `^>= 1.40`, and source+ Shibuya's generic messaging keys from its typed exports.+ ## 0.5.0.0 — 2026-05-05 ### Breaking Changes
shibuya-core.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.14 name: shibuya-core-version: 0.5.0.0+version: 0.6.0.0 synopsis: Supervised queue processing framework for Haskell description: A supervised queue processing framework inspired by Broadway (Elixir).@@ -70,9 +70,9 @@ effectful ^>=2.6.1.0, effectful-core ^>=2.6.1.0, generic-lens ^>=2.3.0.0,- hs-opentelemetry-api ^>=0.3,- hs-opentelemetry-propagator-w3c ^>=0.1,- hs-opentelemetry-semantic-conventions ^>=0.1,+ hs-opentelemetry-api ^>=1.0,+ hs-opentelemetry-propagator-w3c ^>=1.0,+ hs-opentelemetry-semantic-conventions ^>=1.40, lens ^>=5.3.5, nqe ^>=0.6, random ^>=1.2,@@ -128,9 +128,8 @@ bytestring, effectful, hs-opentelemetry-api,- hs-opentelemetry-exporter-in-memory ^>=0.0,+ hs-opentelemetry-exporter-in-memory ^>=1.0, hspec ^>=2.11,- memory, nqe, shibuya-core, stm,
src/Shibuya/Core/Types.hs view
@@ -23,7 +23,7 @@ import Data.ByteString (ByteString) import Data.HashMap.Strict (HashMap) import Data.String (IsString)-import OpenTelemetry.Attributes (Attribute (..), PrimitiveAttribute (..))+import OpenTelemetry.Attributes (Attribute (..)) import Shibuya.Prelude -- | Stable identity for idempotency & observability.
src/Shibuya/Telemetry/Effect.hs view
@@ -38,7 +38,7 @@ ) where -import Control.Exception (Exception, bracket_)+import Control.Exception (Exception, bracket) import Data.ByteString qualified as BS import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict qualified as HashMap@@ -280,10 +280,10 @@ let parentSpan = OTel.wrapSpanContext parentCtx newContext = Ctx.insertSpan parentSpan Ctx.empty withEffToIO (ConcUnlift Persistent Unlimited) $ \runInIO ->- bracket_+ bracket (Ctx.attachContext newContext) Ctx.detachContext- (runInIO action)+ (\_token -> runInIO action) -------------------------------------------------------------------------------- -- Internal Helpers
src/Shibuya/Telemetry/Semantic.hs view
@@ -91,12 +91,12 @@ attrMessagingDestinationName :: Text attrMessagingDestinationName = unkey Sem.messaging_destination_name --- | The messaging operation (@messaging.operation@).+-- | The messaging operation type (@messaging.operation.type@). ----- One of the spec-defined enum values: @publish@, @receive@, @process@,--- @settle@, @create@. Shibuya's per-message span uses @"process"@.+-- One of the spec-defined enum values: @create@, @send@, @receive@,+-- @process@, @settle@. Shibuya's per-message span uses @"process"@. attrMessagingOperation :: Text-attrMessagingOperation = unkey Sem.messaging_operation+attrMessagingOperation = unkey Sem.messaging_operation_type -------------------------------------------------------------------------------- -- Attribute Keys (Shibuya-specific)@@ -129,7 +129,7 @@ -- | A generic partition identifier (@shibuya.partition@). ----- Shibuya-specific: in semantic-conventions v1.24 there is no portable+-- Shibuya-specific: there is no portable -- @messaging.destination.partition.id@ key — only system-specific ones -- (@messaging.kafka.destination.partition@, -- @messaging.eventhubs.destination.partition.id@). System-specific
test/Shibuya/Telemetry/PropagationSpec.hs view
@@ -3,10 +3,10 @@ -- | Tests for W3C Trace Context propagation. module Shibuya.Telemetry.PropagationSpec (spec) where -import Data.ByteArray.Encoding (Base (..)) import Data.ByteString (ByteString) import Effectful (runEff) import OpenTelemetry.Attributes (emptyAttributes)+import OpenTelemetry.Exporter.InMemory (inMemoryListExporter) import OpenTelemetry.Trace.Core ( InstrumentationLibrary (..), createTracerProvider,@@ -17,6 +17,7 @@ tracerOptions, ) import OpenTelemetry.Trace.Core qualified as OTel+import OpenTelemetry.Trace.Id (Base (..)) import OpenTelemetry.Trace.Id qualified as OTel.Id import Shibuya.Telemetry.Effect (runTracing, runTracingNoop, withSpan) import Shibuya.Telemetry.Propagation (currentTraceHeaders, extractTraceContext)@@ -60,38 +61,29 @@ headers = [("traceparent", "invalid-format")] extractTraceContext headers `shouldBe` Nothing - it "parses traceparent with non-standard version (permissive)" $ do- -- Note: hs-opentelemetry propagator is permissive and accepts non-00 versions+ it "rejects traceparent with non-standard version" $ do+ -- hs-opentelemetry 1.0 validates the traceparent version strictly. let headers :: [(ByteString, ByteString)] headers = [ ("traceparent", "ff-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01") ]- -- Library accepts this - validation is left to the application- case extractTraceContext headers of- Nothing -> expectationFailure "Library should parse non-standard version"- Just _ctx -> pure ()+ extractTraceContext headers `shouldBe` Nothing - it "parses traceparent with all-zero trace ID (permissive)" $ do- -- Note: hs-opentelemetry propagator doesn't reject all-zero trace IDs+ it "rejects traceparent with all-zero trace ID" $ do+ -- hs-opentelemetry 1.0 rejects all-zero trace IDs. let headers :: [(ByteString, ByteString)] headers = [ ("traceparent", "00-00000000000000000000000000000000-b7ad6b7169203331-01") ]- -- Library accepts this - validation is left to the application- case extractTraceContext headers of- Nothing -> expectationFailure "Library should parse all-zero trace ID"- Just ctx -> OTel.Id.traceIdBaseEncodedText Base16 (OTel.traceId ctx) `shouldBe` "00000000000000000000000000000000"+ extractTraceContext headers `shouldBe` Nothing - it "parses traceparent with all-zero span ID (permissive)" $ do- -- Note: hs-opentelemetry propagator doesn't reject all-zero span IDs+ it "rejects traceparent with all-zero span ID" $ do+ -- hs-opentelemetry 1.0 rejects all-zero span IDs. let headers :: [(ByteString, ByteString)] headers = [ ("traceparent", "00-0af7651916cd43dd8448eb211c80319c-0000000000000000-01") ]- -- Library accepts this - validation is left to the application- case extractTraceContext headers of- Nothing -> expectationFailure "Library should parse all-zero span ID"- Just ctx -> OTel.Id.spanIdBaseEncodedText Base16 (OTel.spanId ctx) `shouldBe` "0000000000000000"+ extractTraceContext headers `shouldBe` Nothing it "handles unsampled trace flag (00)" $ do let headers :: [(ByteString, ByteString)]@@ -151,11 +143,12 @@ ) tracerOptions result <- runEff $ runTracing tracer currentTraceHeaders- _ <- shutdownTracerProvider provider+ _ <- shutdownTracerProvider provider (Just 5_000_000) result `shouldBe` Nothing it "returns headers carrying the active span's traceparent" $ do- provider <- createTracerProvider [] emptyTracerProviderOptions+ (processor, _spansRef) <- inMemoryListExporter+ provider <- createTracerProvider [processor] emptyTracerProviderOptions let tracer = makeTracer provider@@ -168,7 +161,7 @@ ) tracerOptions result <- runEff $ runTracing tracer $ withSpan "outer" defaultSpanArguments currentTraceHeaders- _ <- shutdownTracerProvider provider+ _ <- shutdownTracerProvider provider (Just 5_000_000) case result of Nothing -> expectationFailure "expected currentTraceHeaders to return Just" Just hs -> case lookup "traceparent" hs of
test/Shibuya/Telemetry/SemanticSpec.hs view
@@ -24,13 +24,16 @@ getAttributeMap, toAttribute, )-import OpenTelemetry.Exporter.InMemory.Span (inMemoryListExporter)+import OpenTelemetry.Exporter.InMemory (inMemoryListExporter) import OpenTelemetry.Trace.Core ( Event (..), ImmutableSpan (..), InstrumentationLibrary (..), createTracerProvider, emptyTracerProviderOptions,+ hotAttributes,+ hotEvents,+ hotName, makeTracer, shutdownTracerProvider, tracerOptions,@@ -77,20 +80,21 @@ _ <- runWithMetrics 4 procId adapter handler pure () - _ <- shutdownTracerProvider provider+ _ <- shutdownTracerProvider provider (Just 5_000_000) spans <- readIORef spansRef case spans of [s] -> do- spanName s `shouldBe` "test-proc process"- let attrs = getAttributeMap (spanAttributes s)+ hot <- readIORef (spanHot s)+ hotName hot `shouldBe` "test-proc process"+ let attrs = getAttributeMap (hotAttributes hot) attrs `shouldHaveTextAttribute` ("messaging.system", "shibuya") attrs `shouldHaveTextAttribute` ("messaging.message.id", "m-1") attrs `shouldHaveTextAttribute` ("messaging.destination.name", "test-proc")- attrs `shouldHaveTextAttribute` ("messaging.operation", "process")+ attrs `shouldHaveTextAttribute` ("messaging.operation.type", "process") attrs `shouldHaveTextAttribute` ("shibuya.ack.decision", "ack_ok") attrs `shouldHaveIntAttribute` ("shibuya.inflight.count", 1) attrs `shouldHaveIntAttribute` ("shibuya.inflight.max", 1)- let evNames = map eventName (toList (appendOnlyBoundedCollectionValues (spanEvents s)))+ let evNames = map eventName (toList (appendOnlyBoundedCollectionValues (hotEvents hot))) evNames `shouldContain` ["shibuya.handler.started"] evNames `shouldContain` ["shibuya.handler.completed"] _ ->@@ -139,11 +143,12 @@ _ <- runWithMetrics 4 procId adapter handler pure () - _ <- shutdownTracerProvider provider+ _ <- shutdownTracerProvider provider (Just 5_000_000) spans <- readIORef spansRef case spans of [s] -> do- let attrs = getAttributeMap (spanAttributes s)+ hot <- readIORef (spanHot s)+ let attrs = getAttributeMap (hotAttributes hot) -- Adapter override wins. attrs `shouldHaveTextAttribute` ("messaging.system", "kafka") -- Adapter-typed attributes appear on the framework span.@@ -151,7 +156,7 @@ attrs `shouldHaveIntAttribute` ("messaging.kafka.message.offset", 42) -- Framework defaults still set where the adapter did not override. attrs `shouldHaveTextAttribute` ("messaging.destination.name", "orders-consumer")- attrs `shouldHaveTextAttribute` ("messaging.operation", "process")+ attrs `shouldHaveTextAttribute` ("messaging.operation.type", "process") attrs `shouldHaveTextAttribute` ("messaging.message.id", "orders-2-42") _ -> expectationFailure $