feat(grpc): Add integration support for GRPC#3721
Conversation
Adds a new `sentry_grpc` package with `SentryGrpcInterceptor`, a gRPC `ClientInterceptor` that provides: - Distributed tracing via `sentry-trace` / `baggage` header injection - Child span creation with `grpc.client` operation per unary call - Breadcrumbs for outgoing RPC calls (method, status, duration) - Optional exception capture for non-OK gRPC status codes - Span-first lifecycle compatibility - Support for grpc >=4.0.4 <6.0.0 Co-Authored-By: Claude <[email protected]>
Each example is a Flutter app with three buttons: - Good Request: GET rsa4096.badssl.com (valid TLS) - Bad Request: GET expired.badssl.com (expired cert, captured by Sentry) - gRPC Request: grpcb.in:9001 GRPCBin/Empty via SentryGrpcInterceptor grpc4 resolves grpc 4.x; grpc5 resolves grpc 5.x. Both produce identical behaviour to demonstrate version-agnostic compatibility. Co-Authored-By: Claude <[email protected]>
DummyUnary sends DummyMessage { f_string: "hello from sentry_grpc" }
and displays the echoed f_string from the response. The message is
hand-encoded via proto wire format (field 1, wire type 2) without
needing a protobuf codegen step.
RandomError triggers a random gRPC error from grpcb.in, which is
automatically captured by SentryGrpcInterceptor and reported to Sentry.
Co-Authored-By: Claude <[email protected]>
- Add captureRequestHeaders option to attach outgoing gRPC metadata to spans, with PII filtering for sensitive headers - Attach gRPC error details (ErrorInfo, BadRequest, RetryInfo, DebugInfo, PreconditionFailure, ResourceInfo, QuotaFailure) to spans on failed calls - Remove captureBody (not captured by other SDK integrations) - Improve example apps: typed DummyMessage, WithHeaders button with custom metadata, SentryFlutter init in grpc4 Co-Authored-By: Claude <[email protected]>
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. FeaturesDart
Other
Enhancements
DependenciesDeps
Internal Changes
🤖 This preview updates automatically when you update the PR. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3721 +/- ##
==========================================
+ Coverage 87.22% 87.33% +0.10%
==========================================
Files 336 337 +1
Lines 12081 12192 +111
==========================================
+ Hits 10538 10648 +110
- Misses 1543 1544 +1
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 |
|---|---|---|---|
| 1980dca | 354.77 ms | 346.96 ms | -7.82 ms |
| f3771ea | 379.02 ms | 363.91 ms | -15.11 ms |
| d3fb366 | 391.49 ms | 385.85 ms | -5.64 ms |
| bfabaf2 | 408.04 ms | 444.38 ms | 36.34 ms |
| eee3b3b | 373.20 ms | 372.54 ms | -0.65 ms |
| 393f8ec | 360.07 ms | 362.70 ms | 2.64 ms |
| e90cf93 | 374.35 ms | 368.40 ms | -5.95 ms |
| e04b24b | 504.72 ms | 516.43 ms | 11.71 ms |
| a5b28db | 383.85 ms | 387.65 ms | 3.80 ms |
| f42e9fb | 360.91 ms | 389.85 ms | 28.94 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 1980dca | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| f3771ea | 14.31 MiB | 15.49 MiB | 1.19 MiB |
| d3fb366 | 13.93 MiB | 15.06 MiB | 1.13 MiB |
| bfabaf2 | 13.93 MiB | 15.06 MiB | 1.13 MiB |
| eee3b3b | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 393f8ec | 13.93 MiB | 15.06 MiB | 1.13 MiB |
| e90cf93 | 14.31 MiB | 15.56 MiB | 1.25 MiB |
| e04b24b | 13.93 MiB | 15.00 MiB | 1.06 MiB |
| a5b28db | 13.93 MiB | 15.18 MiB | 1.25 MiB |
| f42e9fb | 14.55 MiB | 15.87 MiB | 1.31 MiB |
Previous results on branch: lz/grpc
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| acacb94 | 510.81 ms | 512.74 ms | 1.93 ms |
| 247fbe6 | 352.18 ms | 336.59 ms | -15.59 ms |
| a4d676d | 354.69 ms | 354.87 ms | 0.18 ms |
| b44b512 | 347.71 ms | 360.96 ms | 13.25 ms |
| acf9586 | 413.98 ms | 410.58 ms | -3.40 ms |
| a16690d | 401.98 ms | 421.49 ms | 19.51 ms |
| 241da6e | 383.34 ms | 388.47 ms | 5.13 ms |
| 0623fa7 | 362.04 ms | 371.06 ms | 9.02 ms |
| 166b609 | 358.77 ms | 357.23 ms | -1.54 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| acacb94 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 247fbe6 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| a4d676d | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| b44b512 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| acf9586 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| a16690d | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 241da6e | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 0623fa7 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
| 166b609 | 14.55 MiB | 15.87 MiB | 1.31 MiB |
iOS Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| cc4e375 | 1253.06 ms | 1263.81 ms | 10.75 ms |
| c5cf749 | 1231.07 ms | 1236.08 ms | 5.02 ms |
| a34fde0 | 1255.35 ms | 1254.31 ms | -1.03 ms |
| cf443d2 | 1255.79 ms | 1248.38 ms | -7.40 ms |
| 114239b | 1225.74 ms | 1230.17 ms | 4.43 ms |
| 4481076 | 1256.48 ms | 1266.64 ms | 10.17 ms |
| cd94e79 | 1266.86 ms | 1252.40 ms | -14.47 ms |
| f11c8f8 | 1252.43 ms | 1250.17 ms | -2.25 ms |
| 944b773 | 1252.82 ms | 1254.08 ms | 1.27 ms |
| df1842f | 1248.15 ms | 1249.83 ms | 1.68 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| cc4e375 | 7.86 MiB | 9.44 MiB | 1.58 MiB |
| c5cf749 | 5.73 MiB | 6.17 MiB | 455.86 KiB |
| a34fde0 | 5.66 MiB | 6.10 MiB | 451.57 KiB |
| cf443d2 | 5.53 MiB | 6.00 MiB | 479.99 KiB |
| 114239b | 5.53 MiB | 5.96 MiB | 444.85 KiB |
| 4481076 | 7.86 MiB | 9.44 MiB | 1.58 MiB |
| cd94e79 | 5.83 MiB | 6.28 MiB | 459.39 KiB |
| f11c8f8 | 5.83 MiB | 6.28 MiB | 462.27 KiB |
| 944b773 | 5.53 MiB | 6.00 MiB | 479.98 KiB |
| df1842f | 5.83 MiB | 6.28 MiB | 459.42 KiB |
Previous results on branch: lz/grpc
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| acacb94 | 1254.73 ms | 1270.29 ms | 15.56 ms |
| b44b512 | 1249.56 ms | 1252.02 ms | 2.47 ms |
| 241da6e | 1260.35 ms | 1256.17 ms | -4.18 ms |
| 247fbe6 | 1248.98 ms | 1251.13 ms | 2.15 ms |
| 166b609 | 1263.55 ms | 1261.00 ms | -2.55 ms |
| a4d676d | 1256.67 ms | 1256.33 ms | -0.34 ms |
| a16690d | 1255.77 ms | 1260.09 ms | 4.31 ms |
| acf9586 | 1252.91 ms | 1253.07 ms | 0.16 ms |
| 0623fa7 | 1243.60 ms | 1245.25 ms | 1.65 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| acacb94 | 5.83 MiB | 6.28 MiB | 459.60 KiB |
| b44b512 | 5.83 MiB | 6.28 MiB | 459.39 KiB |
| 241da6e | 5.83 MiB | 6.28 MiB | 459.59 KiB |
| 247fbe6 | 5.83 MiB | 6.28 MiB | 460.84 KiB |
| 166b609 | 5.83 MiB | 6.28 MiB | 462.27 KiB |
| a4d676d | 5.83 MiB | 6.28 MiB | 462.27 KiB |
| a16690d | 5.83 MiB | 6.28 MiB | 459.40 KiB |
| acf9586 | 5.83 MiB | 6.28 MiB | 461.61 KiB |
| 0623fa7 | 5.83 MiB | 6.28 MiB | 462.27 KiB |
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Remove Flutter dependency, platform folders (linux/android/web), and empty widget tests. Examples now run with `dart run lib/main.dart`. Co-Authored-By: Claude <[email protected]>
Flutter tool file, not needed for building or running. Co-Authored-By: Claude <[email protected]>
buenaflor
left a comment
There was a problem hiding this comment.
Did a quick PR review for now (didn't look into the details yet)
- Remove manual CHANGELOG entry (auto-generated from PR title) - Add gRPC error detail attribute constants to SemanticAttributesConstants - Use constants in interceptor instead of raw strings - Wrap fire-and-forget response.then() with unawaited() - Capture StackTrace in onError and pass to captureException - Gate ErrorInfo.metadata behind sendDefaultPii - Check tracePropagationTargets before injecting trace headers - Fix noSuchMethod to throw UnsupportedError instead of silently dropping - Add tests for _attachErrorDetails (ErrorInfo, BadRequest, RetryInfo, DebugInfo, ResourceInfo) including PII guard for metadata - Add tests for tracePropagationTargets header injection control Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
The interceptor already captures failed requests via captureFailedRequests: true, so manual captureException calls in catch blocks caused each error to be reported twice. Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit af1b816. Configure here.
buenaflor
left a comment
There was a problem hiding this comment.
nice! one last thing about the version then we're good
## DESCRIBE YOUR PR Adds documentation for the upcoming `sentry_grpc` package introduced in [getsentry/sentry-dart#3721](getsentry/sentry-dart#3721). - Adds `includes/dart-integrations/grpc.mdx` with full integration docs (requirements, install, configure, failed request capture, breadcrumbs, tracing, verify) ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. - [ ] Urgent deadline (GA date, etc.): <!-- ENTER DATE HERE --> - [ ] Other deadline: <!-- ENTER DATE HERE --> - [x] None: Not urgent, can wait up to 1 week+ ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've added an urgent due date to it. Thanks in advance for your help! ## PRE-MERGE CHECKLIST *Make sure you've checked the following before merging your changes:* - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) Close getsentry/sentry-dart#3754 --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]> Co-authored-by: Giancarlo Buenaflor <[email protected]> Co-authored-by: Giancarlo Buenaflor <[email protected]>
buenaflor's original review comment on #3721 asked to move the dart package's fuller MockHub (not grpc's stripped-down copy) into _sentry_testing, so integration packages get the full Hub surface instead of a minimal reimplementation. Replaces the previously-moved minimal grpc MockHub in _sentry_testing with the dart package's version, self-contained (own default SentryOptions instead of test_utils.dart's, local CaptureTransactionCall/CaptureLogCall instead of importing mock_sentry_client.dart). Repoints all 19 dart-package test files from the deleted local mock_hub.dart to the shared package. Co-Authored-By: Claude <[email protected]>
* refactor(grpc): Move MockHub to _sentry_testing package The grpc package's MockHub and NoSuchMethodProvider duplicated the same pattern already used across other integration packages' tests. Moving them into the shared _sentry_testing package (already a grpc dev dependency for FakeTelemetryProcessor) lets other integration packages reuse them without copy-pasting. Closes #3749 Co-Authored-By: Claude <[email protected]> * refactor(dart): Use the dart package's MockHub as the shared one buenaflor's original review comment on #3721 asked to move the dart package's fuller MockHub (not grpc's stripped-down copy) into _sentry_testing, so integration packages get the full Hub surface instead of a minimal reimplementation. Replaces the previously-moved minimal grpc MockHub in _sentry_testing with the dart package's version, self-contained (own default SentryOptions instead of test_utils.dart's, local CaptureTransactionCall/CaptureLogCall instead of importing mock_sentry_client.dart). Repoints all 19 dart-package test files from the deleted local mock_hub.dart to the shared package. Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Giancarlo Buenaflor <[email protected]>

📜 Summary
ErrorInfo,BadRequest,RetryInfo,DebugInfo,PreconditionFailure,ResourceInfo,QuotaFailure) to spans on failed callscaptureBody— no other Sentry SDK gRPC integration captures the request bodyDummyMessageclass (replaces rawList<int>hand-encoding),WithHeadersbutton demonstrating custom gRPC metadata,SentryFlutterinit in grpc4 example💡 Motivation and Context
Brings the Dart gRPC interceptor closer to parity with the Python and JS integrations for request header propagation, while adding richer error detail capture that those SDKs don't have.
💚 How did you test it?
grpcb.inhttp.request.header.*data when metadata is set📝 Checklist
sendDefaultPiiis enabled🔮 Next steps
Close #1120