Skip to content

fix: telemetry dropped by user callbacks and by validation is now counted in client reports - #5607

Merged
jamescrosswell merged 13 commits into
mainfrom
fix/callback-client-reports
Sep 28, 2026
Merged

jamescrosswell merged 13 commits into
mainfrom
fix/callback-client-reports

Conversation

@jamescrosswell

@jamescrosswell jamescrosswell commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

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-callbacks and 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)

Callback Returns null Throws
Event processor event_processor (unchanged) callback_error + the caller's category, item dropped
Transaction processor event_processor (unchanged) callback_error / transaction + span, transaction dropped
BeforeSendFeedback before_send (unchanged) callback_error / feedback (was before_send)
BeforeSendLog before_send / log_item callback_error / log_item
configureLog can't return null callback_error / log_item
BeforeSendMetric before_send / trace_metric callback_error / trace_metric

A null return is an explicit drop and keeps its usual reason. A throw is recorded as callback_error, so users can tell a failing callback apart from one that dropped the item on purpose. This follows the hooks spec revision in b53c3009. The spec PR is still open, and its current text makes this report a SHOULD.

callback_error is already in the stable client reports spec (1.24.0). This adds it to DiscardReason, and Relay accepts it for the same reason it accepts invalid (see below).

Validation drops (adjacent, same files)

Path Now records
Log template doesn't match its arguments (FormatException) invalid / log_item
Metric value type unsupported (both CaptureMetric overloads) invalid / trace_metric
Metric name null or empty (both overloads) invalid / trace_metric

invalid — "Failed validation" — is in the client reports spec but was one of six reasons the SDK's DiscardReason didn't carry; this adds it. Relay accepts it: DiscardedEvent.reason is a free-form String, and discarded_events maps to Outcome::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:

  • Processors: the report was being skipped by control flow, not by omission. SentryEventHelper.ProcessEvent already recorded event_processor when a processor returned null; a processor that threw jumped straight past that branch to Hub.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.
  • Transaction processors are included even though the issue only lists event processors. The loop in SentryClient.CaptureTransaction has the identical defect against the identical catch-all, and the spec requirement is the same.
  • configureLog isn't named in the spec (it's the Action<SentryLog> overload parameter, already marked "will be removed in a future version"). It's still a user callback, so a throw is recorded as callback_error like the others. It can't return null, so there's no explicit-drop case.
  • The validation drops are not spec-conformance work; they're the same "telemetry lost with no report" defect sitting in the two files this PR already touches, so they're fixed here rather than left for a follow-up that would touch them again.
  • Work item 3 (BeforeSend/BeforeSendTransaction alignment) 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

jamescrosswell and others added 3 commits September 22, 2026 12:31
…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

codecov Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.23529% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.93%. Comparing base (5e6a369) to head (171cc52).

Files with missing lines Patch % Lines
src/Sentry/Internal/DefaultSentryMetricEmitter.cs 70.00% 2 Missing and 1 partial ⚠️
...c/Sentry/Internal/DefaultSentryStructuredLogger.cs 87.50% 0 Missing and 1 partial ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamescrosswell

Copy link
Copy Markdown
Collaborator Author

Still not reported: the FormatException path in DefaultSentryStructuredLogger (template/argument mismatch) also drops a log without a client report. That's a user input error rather than a callback failure, so it's outside this issue's scope — flagging it as worth a follow-up.

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>
@jamescrosswell jamescrosswell changed the title fix: telemetry dropped by user callbacks is now counted in client reports fix: telemetry dropped by user callbacks and by validation is now counted in client reports Sep 23, 2026
Comment thread src/Sentry/Internal/DefaultSentryMetricEmitter.cs
Comment thread src/Sentry/Internal/DefaultSentryStructuredLogger.cs
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>
@jamescrosswell
jamescrosswell marked this pull request as ready for review September 23, 2026 04:16
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Sep 23, 2026
@jamescrosswell
jamescrosswell added this pull request to stack #5618 September 23, 2026 08:04
Comment thread src/Sentry/SentryClient.cs Outdated
jamescrosswell and others added 2 commits September 24, 2026 10:18
…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>

@ric-oliv ric-oliv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@jamescrosswell
jamescrosswell removed this pull request from stack #5618 September 27, 2026 22:01
@jamescrosswell
jamescrosswell added this pull request to stack #5628 September 27, 2026 22:01
@jamescrosswell
jamescrosswell removed this pull request from stack #5628 September 27, 2026 22:01
Base automatically changed from fix/isolate-user-callbacks to main September 28, 2026 22:02
@jamescrosswell
jamescrosswell merged commit cc9ec24 into main Sep 28, 2026
41 of 42 checks passed
@jamescrosswell
jamescrosswell deleted the fix/callback-client-reports branch September 28, 2026 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conform to the callback error isolation spec (wrap all user callbacks)

2 participants