feat(flutter): Add replay trace ID sync for Android#3744
Conversation
Register Dart transaction and segment trace IDs with native Android replay so replay segments can be discovered by trace ID. Closes #3729 Co-Authored-By: GPT-5.5 <[email protected]> Co-authored-by: Cursor <[email protected]>
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. Features
Fixes
Dependencies
Internal Changes
🤖 This preview updates automatically when you update the PR. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3744 +/- ##
==========================================
+ Coverage 86.98% 91.93% +4.95%
==========================================
Files 336 105 -231
Lines 11997 3758 -8239
==========================================
- Hits 10435 3455 -6980
+ Misses 1562 303 -1259
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
Android Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| f11c8f8 | 360.00 ms | 348.43 ms | -11.57 ms |
| e1ab497 | 429.30 ms | 439.00 ms | 9.70 ms |
| 1e46846 | 419.06 ms | 429.65 ms | 10.59 ms |
| 2cf9161 | 454.12 ms | 512.67 ms | 58.55 ms |
| 396cb30 | 405.70 ms | 427.56 ms | 21.86 ms |
| 667e3c4 | 402.86 ms | 394.04 ms | -8.82 ms |
| e8f0fc4 | 348.40 ms | 353.34 ms | 4.94 ms |
| 40751bf | 421.09 ms | 424.82 ms | 3.73 ms |
| eca355d | 485.50 ms | 569.85 ms | 84.35 ms |
| 1f639ee | 429.98 ms | 476.60 ms | 46.62 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| f11c8f8 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| e1ab497 | 13.93 MiB | 15.06 MiB | 1.13 MiB |
| 1e46846 | 14.31 MiB | 15.56 MiB | 1.25 MiB |
| 2cf9161 | 6.54 MiB | 7.70 MiB | 1.16 MiB |
| 396cb30 | 13.93 MiB | 15.06 MiB | 1.13 MiB |
| 667e3c4 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| e8f0fc4 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 40751bf | 14.31 MiB | 15.49 MiB | 1.19 MiB |
| eca355d | 6.54 MiB | 7.70 MiB | 1.16 MiB |
| 1f639ee | 13.93 MiB | 15.00 MiB | 1.06 MiB |
Previous results on branch: buenaflor/feat/searchable-trace-replay
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 51e2c94 | 374.19 ms | 386.85 ms | 12.66 ms |
| e5db8cc | 353.67 ms | 332.43 ms | -21.24 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 51e2c94 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| e5db8cc | 14.55 MiB | 15.87 MiB | 1.31 MiB |
Register native replay trace IDs when the Dart propagation trace is created or regenerated. This keeps replay telemetry aligned with the trace lifecycle even when no span or transaction is captured. Co-Authored-By: GPT-5.5 <[email protected]> Co-authored-by: Cursor <[email protected]>
Remove the internal transaction-captured lifecycle callback now that no integration consumes it. This avoids keeping dead callback surface after replay trace registration moved to trace generation. Co-Authored-By: GPT-5.5 <[email protected]> Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
iOS Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9d516f3 | 1256.04 ms | 1262.70 ms | 6.66 ms |
| eca355d | 1238.39 ms | 1266.98 ms | 28.59 ms |
| d0aa4b6 | 1268.23 ms | 1268.39 ms | 0.15 ms |
| 29e8ebe | 1260.46 ms | 1262.60 ms | 2.14 ms |
| 8bfae64 | 1237.43 ms | 1238.94 ms | 1.51 ms |
| 5fc86f6 | 1245.91 ms | 1248.54 ms | 2.63 ms |
| 3f47ea3 | 1263.90 ms | 1263.87 ms | -0.02 ms |
| ec78888 | 1251.37 ms | 1269.40 ms | 18.04 ms |
| 7cfbbd6 | 1270.63 ms | 1285.36 ms | 14.72 ms |
| 75284dc | 1254.81 ms | 1262.28 ms | 7.46 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9d516f3 | 5.73 MiB | 6.18 MiB | 463.54 KiB |
| eca355d | 7.86 MiB | 9.44 MiB | 1.58 MiB |
| d0aa4b6 | 5.53 MiB | 6.02 MiB | 502.04 KiB |
| 29e8ebe | 5.53 MiB | 6.01 MiB | 488.12 KiB |
| 8bfae64 | 5.66 MiB | 6.10 MiB | 451.85 KiB |
| 5fc86f6 | 5.83 MiB | 6.28 MiB | 462.37 KiB |
| 3f47ea3 | 5.53 MiB | 5.96 MiB | 444.81 KiB |
| ec78888 | 7.86 MiB | 9.44 MiB | 1.58 MiB |
| 7cfbbd6 | 7.86 MiB | 9.44 MiB | 1.58 MiB |
| 75284dc | 5.53 MiB | 5.97 MiB | 453.77 KiB |
Previous results on branch: buenaflor/feat/searchable-trace-replay
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 51e2c94 | 1273.14 ms | 1271.37 ms | -1.78 ms |
| e5db8cc | 1257.61 ms | 1266.47 ms | 8.86 ms |
| fc7b45c | 1258.74 ms | 1268.50 ms | 9.76 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 51e2c94 | 5.83 MiB | 6.28 MiB | 461.61 KiB |
| e5db8cc | 5.83 MiB | 6.28 MiB | 461.60 KiB |
| fc7b45c | 5.83 MiB | 6.28 MiB | 461.62 KiB |
Restore the lifecycle test fixture to match main while keeping the transaction-captured callback removed from this branch. Co-Authored-By: GPT-5.5 <[email protected]> Co-authored-by: Cursor <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR adds a Flutter-to-native Android bridge for replay trace ID synchronization by introducing a new registerTraceId binding and wiring it into the replay telemetry integration via SDK lifecycle events, so native replay segments can be associated with Dart/Flutter trace IDs for searchability.
Changes:
- Adds
registerTraceId(SentryId traceId)toSentryNativeBindingand implements it for Android via JNI (SentryNativeJava), with no-op implementations for other platforms. - Updates
ReplayTelemetryIntegrationto register the initial trace ID and newly generated trace IDs (viaOnGenerateNewTrace) with the native binding. - Extends Flutter tests/mocks to validate forwarding behavior and callback cleanup.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/flutter/lib/src/native/sentry_native_binding.dart | Adds the new registerTraceId API to the native binding contract. |
| packages/flutter/lib/src/native/java/sentry_native_java.dart | Implements registerTraceId through the JNI replay integration on Android. |
| packages/flutter/lib/src/integrations/replay_telemetry_integration.dart | Registers initial/new trace IDs with native replay using lifecycle callbacks. |
| packages/flutter/lib/src/web/sentry_web.dart | Adds a no-op registerTraceId implementation for web. |
| packages/flutter/lib/src/native/sentry_native_channel.dart | Adds a no-op registerTraceId implementation for method-channel platforms. |
| packages/flutter/lib/src/native/c/sentry_native.dart | Adds a no-op registerTraceId implementation for the C/FFI binding. |
| packages/flutter/test/integrations/replay_telemetry_integration_test.dart | Adds test coverage for trace ID registration and callback removal. |
| packages/flutter/test/mocks.mocks.dart | Updates generated mock to include registerTraceId. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Await native replay trace registration so lifecycle dispatch does not complete before async native bindings finish syncing the trace ID. Co-Authored-By: GPT-5.5 <[email protected]> Co-authored-by: Cursor <[email protected]>
| final spanId = SpanId.newId(); | ||
|
|
||
| await fixture.getSut().call(fixture.hub, fixture.options); | ||
| clearInteractions(fixture.nativeBinding); |
There was a problem hiding this comment.
Why do we need to clear interactions here?
There was a problem hiding this comment.
because when the integration is called we register the trace id, without this we would have two calls because we trigger the callback
📜 Description
Register Dart transaction and stream segment trace IDs with native Android replay through the new
registerTraceIdJNI binding. This lets native replay segments collecttrace_idsfor Flutter app activity so replays can be found by trace ID.💡 Motivation and Context
Flutter delegates replay recording to the native SDK, but Dart-traceId were not forwarded to native replay for trace ID collection. The SDK now dispatches an internal lifecycle event after Dart transactions are captured and lets replay telemetry register transaction and segment trace IDs with the native binding.
Partly resolves #3729 - only for Android currently
💚 How did you test it?
fvm dart test test/sentry_client_sdk_lifecycle_test.dartfvm flutter test test/integrations/replay_telemetry_integration_test.dartfvm flutter test test/integrations/native_trace_sync_integration_test.dartfvm dart analyze lib/src/sdk_lifecycle_hooks.dart lib/src/sentry_client.dart test/sentry_client_sdk_lifecycle_test.dartfvm flutter analyze lib/src/integrations/replay_telemetry_integration.dart lib/src/native/sentry_native_binding.dart lib/src/native/java/sentry_native_java.dart lib/src/native/c/sentry_native.dart lib/src/native/sentry_native_channel.dart lib/src/web/sentry_web.dart test/integrations/replay_telemetry_integration_test.dart test/mocks.mocks.dart📝 Checklist
sendDefaultPiiis enabled🔮 Next steps
Native Android behavior is covered through the generated JNI binding and existing native SDK implementation; the Dart/Flutter tests cover lifecycle dispatch and adapter forwarding.
Made with Cursor