fix: telemetry dropped by user callbacks and by validation is now counted in client reports - #5607
Conversation
…creenshotCapture no longer reach the application Completes the callback-isolation work item from #5535 that #5545 started for TracesSampler. Each callback now runs inside a recovery boundary that logs an error naming the callback and applies the spec's fallback: - BeforeBreadcrumb (Scope.AddBreadcrumb, Android JNI, Cocoa) drops the breadcrumb - ILogEntryFilter.Filter treats a failing filter as "did not filter" - SetBeforeScreenshotCapture skips the screenshot and keeps the event Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ndroid device tests Addresses review feedback on #5606: distinguish native callback failures from managed ones in the diagnostic log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Work item 2 of #5535. Items lost to a user callback — whether it returned null or threw — were disappearing without a client report, and a throwing event or transaction processor skipped the report entirely by unwinding to the Hub catch-all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## fix/isolate-user-callbacks #5607 +/- ##
==============================================================
+ Coverage 74.89% 74.93% +0.04%
==============================================================
Files 515 515
Lines 18966 18994 +28
Branches 3696 3698 +2
==============================================================
+ Hits 14204 14234 +30
Misses 3879 3879
+ Partials 883 881 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Let's do that in this PR as well... no reason to do it in a follow up (the size of the PR is still manageable). |
A log whose template doesn't match its arguments, and a metric with an unsupported value type or an empty name, were dropped with a diagnostic log and no client report. Records the spec's `invalid` reason, which the SDK did not previously carry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every other deliberate drop by a user callback logs at info level naming the callback; logs and metrics were the exception. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ng it through Review feedback on #5606. Failing open meant a filter written to exclude entries would, once broken, start sending them. Before this PR the exception reached the app and nothing was sent, so dropping keeps that outcome while removing the crash, and matches the filter rule applied to BeforeSend in #5610. Also aligns the internal screenshot callback names with the public SetBeforeScreenshotCapture, and fixes a doc sample naming a method that no longer exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ShouldCaptureEvent and ShouldAddBreadcrumb each ran the whole filter chain, so a user filter was invoked twice for every entry that cleared both thresholds — and, since the previous commit, logged its failure twice. The two predicates now test only their level threshold, and the shared guard runs once in Log. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dd74bf4 to
7710900
Compare
… fix/callback-client-reports
ric-oliv
left a comment
There was a problem hiding this comment.
Hi @jamescrosswell, the hooks spec was revised today (getsentry/sentry-docs#19189, b53c3009) 😅
A callback that throws must now be reported with the callback_error discard reason, so users can tell a failure apart from an explicit drop.
The catch blocks in this PR now need to record callback_error instead of before_send / event_processor. null returns stay as they are. callback_error is already in the stable client reports spec, so we just need a new DiscardReason here.
Same in the throw paths in #5610.
Follows the hooks spec revision in getsentry/sentry-docs#19189 (b53c3009): a throwing callback must be distinguishable from an explicit drop, so every item a failure drops is recorded as callback_error with the item's category. Null returns keep their existing reasons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GitHub stopped rebuilding refs/pull/5607/merge, so no pull_request workflow could be created and the PR reported a conflict that git shows does not exist. A new head commit forces the ref to be recomputed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… fix/callback-client-reports # Conflicts: # src/Sentry/SentryClient.cs
Work item 2 ("emit the missing client reports") of #5535, plus the adjacent validation drops in the same files. Stacked on #5606 — review that one first; this branch targets
fix/isolate-user-callbacksand the diff here is only the five source files below.Twelve drop paths were losing telemetry with no client report at all, so a user filtering or mis-calling these APIs saw nothing in their org's client reports.
Callback failures (work item 2)
nullevent_processor(unchanged)callback_error+ the caller's category, item droppedevent_processor(unchanged)callback_error/transaction+span, transaction droppedBeforeSendFeedbackbefore_send(unchanged)callback_error/feedback(wasbefore_send)BeforeSendLogbefore_send/log_itemcallback_error/log_itemconfigureLognullcallback_error/log_itemBeforeSendMetricbefore_send/trace_metriccallback_error/trace_metricA
nullreturn is an explicit drop and keeps its usual reason. A throw is recorded ascallback_error, so users can tell a failing callback apart from one that dropped the item on purpose. This follows the hooks spec revision inb53c3009. The spec PR is still open, and its current text makes this report a SHOULD.callback_erroris already in the stable client reports spec (1.24.0). This adds it toDiscardReason, and Relay accepts it for the same reason it acceptsinvalid(see below).Validation drops (adjacent, same files)
FormatException)invalid/log_itemCaptureMetricoverloads)invalid/trace_metricinvalid/trace_metricinvalid— "Failed validation" — is in the client reports spec but was one of six reasons the SDK'sDiscardReasondidn't carry; this adds it. Relay accepts it:DiscardedEvent.reasonis a free-formString, anddiscarded_eventsmaps toOutcome::ClientDiscard(reason.into())with no allowlist (relay-server/src/processing/client_reports/process.rs). Conversion is per-outcome, so an unrecognised reason could never take a whole report down.Notes for review:
SentryEventHelper.ProcessEventalready recordedevent_processorwhen a processor returned null; a processor that threw jumped straight past that branch toHub.CaptureEvent's catch-all, which records nothing and logs"Failure to capture event"— naming the capture rather than the processor. This is precisely the trap the Linear write-up calls out. The per-processor catch now records the discard and names the processor.SentryClient.CaptureTransactionhas the identical defect against the identical catch-all, and the spec requirement is the same.configureLogisn't named in the spec (it's theAction<SentryLog>overload parameter, already marked "will be removed in a future version"). It's still a user callback, so a throw is recorded ascallback_errorlike the others. It can't returnnull, so there's no explicit-drop case.BeforeSend/BeforeSendTransactionalignment) of Conform to the callback error isolation spec (wrap all user callbacks) #5535 is still open, so this doesn't close the issue.🤖 Generated with Claude Code