nri-prelude: thread an analytics callback through LogHandler#152
Merged
Conversation
0fdd622 to
34a5d26
Compare
34a5d26 to
cb97f14
Compare
Adds an opaque (Aeson.Value -> IO ()) callback to LogHandler, propagated by mkHandler to every child handler. rootTracingSpanIO gains a new parameter for the callback; nullHandler defaults to silentTrack. Re-exports silentTrack from Platform for callers. This is a breaking change to rootTracingSpanIO and mkHandler. See the design doc at NoRedInk/event-platform/docs/2026-05-07-haskell-analytics-tracking-design.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cb97f14 to
236128f
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds an analytics callback pathway to nri-prelude tracing/log handling so Task code can emit JSON analytics events through the ambient LogHandler, while updating sibling packages for the breaking rootTracingSpanIO signature and refreshed golden outputs.
Changes:
- Adds
trackAnalyticsEventIO,silentTrack, andPlatform.Analytics.Internal.trackEvent. - Updates
rootTracingSpanIO/mkHandlercall sites to pass an analytics callback. - Bumps
nri-preludeto0.7.0.0and relaxes sibling package upper bounds.
Reviewed changes
Copilot reviewed 81 out of 81 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| nri-prelude/src/Platform/Internal.hs | Adds analytics callback storage/propagation and updates root span API. |
| nri-prelude/src/Platform/Analytics/Internal.hs | Adds internal analytics tracking task API. |
| nri-prelude/src/Platform.hs | Re-exports silentTrack. |
| nri-prelude/src/Test/Internal.hs | Updates internal test runner root span call. |
| nri-prelude/tests/PlatformSpec.hs | Adds analytics callback tests and updates root span helper. |
| nri-prelude/tests/LogSpec.hs | Updates mkHandler test helper. |
| nri-prelude/package.yaml | Bumps version and exposes analytics module. |
| nri-prelude/nri-prelude.cabal | Mirrors version/module updates. |
| nri-prelude/CHANGELOG.md | Documents 0.7.0.0 changes. |
| nri-kafka/src/Kafka/Worker/Internal.hs | Updates root tracing call with no-op analytics callback. |
| nri-kafka/src/Kafka/Worker/Partition.hs | Updates message processing root tracing call. |
| nri-kafka/package.yaml | Relaxes nri-prelude upper bound. |
| nri-kafka/nri-kafka.cabal | Relaxes several nri-prelude bounds. |
| nri-http/test/Main.hs | Updates test root tracing call. |
| nri-http/package.yaml | Relaxes nri-prelude bounds. |
| nri-http/nri-http.cabal | Mirrors bound updates. |
| nri-redis/test/Spec/Redis.hs | Updates Redis observability test root spans. |
| nri-redis/package.yaml | Relaxes nri-prelude bound. |
| nri-redis/nri-redis.cabal | Mirrors bound updates. |
| nri-postgresql/test/ObservabilitySpec.hs | Updates PostgreSQL observability test root span. |
| nri-postgresql/package.yaml | Relaxes nri-prelude bound. |
| nri-postgresql/nri-postgresql.cabal | Mirrors bound updates. |
| nri-observability/scripts/memory-leak-test/Main.hs | Updates script root tracing call. |
| nri-observability/package.yaml | Relaxes nri-prelude bound. |
| nri-observability/nri-observability.cabal | Mirrors bound updates. |
| nri-env-parser/package.yaml | Relaxes nri-prelude bound. |
| nri-env-parser/nri-env-parser.cabal | Mirrors bound updates. |
| nri-log-explorer/package.yaml | Relaxes nri-prelude bound. |
| nri-log-explorer/nri-log-explorer.cabal | Mirrors bound update. |
| nri-test-encoding/package.yaml | Relaxes nri-prelude bound. |
| nri-test-encoding/nri-test-encoding.cabal | Mirrors bound updates. |
| nri-prelude/tests/golden-results-9.8/* | Refreshes GHC 9.8 golden outputs for version/line changes. |
| nri-prelude/tests/golden-results-9.10/* | Refreshes GHC 9.10 golden outputs for version changes. |
| nri-prelude/tests/golden-results-9.12/* | Refreshes GHC 9.12 golden outputs for version changes. |
| nri-redis/test/golden-results-9.8/* | Refreshes Redis golden source locations after call-site changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
236128f to
2bdf9f9
Compare
trackEvent opens an analytics.track child span, attaches the JSON payload as span details, and invokes the LogHandler's analytics callback. The .Internal suffix signals do-not-import from product code; consumers should wrap this in a typed entry point. Bump to 0.7.0.0 (breaking: rootTracingSpanIO signature changed in the previous commit). Bump nri-prelude upper bound to <0.8 in the sibling packages so the workspace builds, and refresh hard-coded package strings in the test-suite golden files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2bdf9f9 to
35bc0b8
Compare
ArthurJordao
approved these changes
May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Large changeset but easy to review, I promise.
Context
We're introducing analytics events to our Haskell backend.
Compared to tracing spans, those events should have a one-notch-higher level of deliverability guarantees. It's not vital that they are delivered, but it shouldn't be easy to drop them.
We decided to go for a design where they are synchronously delivered at the time they're emitted, and where you don't have to carry around an
AnalyticsHandlerthroughout the call stack.This spells out changes in our Platform.
How we did it
Platform gets a
track event :: Task err (), bypassing the need for threading a handler argument through every function that wants to emit. Matches the ergonomics ofLog.withContext— ambient context carried byTask, no explicit handler in user code.The mechanism is generic.
Platform.Analytics.Internal.trackEventtakes anyToJSONvalue, opens ananalytics.trackchild tracing span, attaches the JSON as span details, and invokes aLogHandler-carriedAeson.Value -> Task Never ()callback. The callback runs as aTaskagainst the surroundingLogHandler— so a wire-layer implementation canLog.erroron delivery failure, open its own child spans, and generally participate in the existing observability pipeline. TheNevererror type encodes the contract that analytics failure never fails the outer track call; downstream is expected to swallow and log its own failures.What changes
LogHandlergains atrackAnalyticsEvent :: Aeson.Value -> Task Never ()field, propagated throughmkHandlerto every child handler.rootTracingSpanIOgains the callback as a new parameter. Breaking change.silentTrackships as the no-op default for callers who don't want analytics.Platform.Analytics.InternalexposestrackEvent :: ToJSON e => e -> Task err (). The.Internalsuffix signals it's not meant for direct product-code use; applications should wrap it behind a typed entry point that enforces their event dictionary at the type level. Haskell has no cross-package import fence, so the convention + downstream lint rules are the available enforcement.Platform.silentTrack(nri-kafka,nri-http,nri-redis,nri-postgresql,nri-observability) bump theirnri-preludebound to>= 0.7.0.0 && < 0.8. The rest just bump the upper bound to< 0.8. Also fixesnri-kafka'ssync-write-benchmarkstanza, which the previous hpack pass left at<0.7.0.7.0.0to flag the breaking change.nri-prelude(version-bump string changes across 9.8/9.10/9.12) andnri-redis(source-line shifts from the newsilentTrackarg inspanForTask/spanForFailingTask, plus the version-string bump after rebasing onto1adf200).Test plan
cabal build allgreencabal test nri-prelude— 278 passed; new test verifies theanalytics.trackchild span carries the JSON payload asdetails, on top of the existing callback-invocation testcabal test nri-redis— 100 passed; goldens regenerated for the new line numbers🤖 PR drafted with Claude Code