@hackage / kiroku-otel

OpenTelemetry W3C trace-context helpers for Kiroku event metadata

Latest0.2.0.1

Changelog

kiroku-otel changelog

Unreleased

0.2.0.1 — 2026-07-11

Other Changes
  • Requires kiroku-store ^>=0.3. The tracer matches exhaustively on KirokuEvent, so it now handles that release's new KirokuEventPublisherLoopError constructor; the event is ignored rather than turned into a span, since the publisher loop is not a per-subscription unit of work. No change to kiroku-otel's own API.

0.2.0.0 — 2026-05-31

Breaking Changes
Complete FSM-state span coverage (Kiroku.Otel.Subscription, MasterPlan 7 EP-2)
  • The per-batch span is now keyed on the new KirokuEventSubscriptionDelivered event and named kiroku.subscription.deliver (replacing the spanFetch / kiroku.subscription.fetch span). It is emitted once per delivered batch on every target ($all, category, consumer group) and in both phases, carrying kiroku.batch.rows and a kiroku.subscription.state of "catchup" or "live". This closes the gap where an $all subscription's live phase produced no per-batch span. KirokuEventSubscriptionFetched is now ignored by the tracer (matched for exhaustiveness) so the DB-driven live path does not emit two spans per batch.
  • kiroku.subscription.stopped is always emitted on Stopped (carrying kiroku.subscription.stop_reason and kiroku.checkpoint.global_position), so the terminal state appears in the trace even for a healthy worker that stops from Live with no open episode span.
  • Internal: the per-key span state is striped from one shared MVar into a lock-free IORef (Map SpanKey (IORef OpenState)) (each key single-writer; the outer registry mutated only on Started/Stopped), so the now-per-batch span work never serializes workers on a shared lock. base-only, no public API change, behavior-identical.
  • New exported span-name constants spanDeliver / spanStopped (replacing spanFetch). A database-backed end-to-end test now runs a real $all worker with the tracer installed and asserts the catch-up, live deliver, and stopped spans all appear.
New Features
Subscription-state tracing (Kiroku.Otel.Subscription, MasterPlan 6 EP-5)
  • subscriptionTraceHandler :: Tracer -> IO (KirokuEvent -> IO ()) — a ready-made eventHandler callback that turns the subscription worker's finite-state lifecycle into OpenTelemetry spans. Install it on ConnectionSettings (or a per-subscription) eventHandler and every subscription emits spans.
  • Short, promptly-ending spans (the SDK only exports a span on endSpan, so there is no worker-lifetime span): per-episode kiroku.subscription.catchup / paused / reconnecting / retrying, per-batch kiroku.subscription.fetch, and standalone kiroku.subscription.dead_letter / db_error. Each carries a kiroku.* attribute set (subscription name, state, consumer-group member/size, checkpoint position, attempt, batch rows, event position, dead-letter/stop reason); the span-name and attribute-key constants are exported.
  • The library remains separate from kiroku-store, so kiroku-store gains no OpenTelemetry dependency. The handler keeps its open spans in a thread-safe MVar keyed by (subscription name, member). Requires a batch span processor so the synchronous callback never blocks the worker on export.
Other Changes
OpenTelemetry 1.0 and current semantic-convention keys
  • kiroku-otel now depends on the OpenTelemetry 1.0 package family: hs-opentelemetry-api ^>=1.0, hs-opentelemetry-propagator-w3c ^>=1.0, and hs-opentelemetry-semantic-conventions ^>=1.40.
  • Subscription spans now emit generated OpenTelemetry semantic-convention attribute keys alongside the existing kiroku.* keys. Delivery spans include messaging.system = "kiroku", messaging.destination.name, messaging.operation.type = "process", and messaging.batch.message_count; database-error spans include db.system.name = "postgresql" and db.operation.name.

0.1.0.0 — 2026-05-23

New Features
  • Initial release. Exposes Kiroku.Otel.TraceContext with two helpers:
    • injectTraceContext :: SpanContext -> EventData -> EventData — encodes the supplied SpanContext to W3C traceparent / tracestate strings and merges them into the event's metadata JSON object. Existing keys in metadata are preserved.
    • extractTraceContext :: RecordedEvent -> Maybe SpanContext — reads the same JSON keys back out and decodes them through OpenTelemetry.Propagator.W3CTraceContext.decodeSpanContext. Returns Nothing when metadata is absent, is not a JSON object, lacks a traceparent key, or carries an unparseable value. Never throws.