Skip to content

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

Open
jamescrosswell wants to merge 6 commits into
fix/isolate-user-callbacksfrom
fix/callback-client-reports
Open

jamescrosswell wants to merge 6 commits into
fix/isolate-user-callbacksfrom
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.

The spec is explicit that "isolating a callback without reporting the loss is a conformance failure". 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)

Path Now records
BeforeSendLog returns null / throws before_send / log_item
configureLog throws before_send / log_item
BeforeSendMetric returns null / throws before_send / trace_metric
Event processor throws event_processor + the caller's category, event dropped
Transaction processor throws event_processor / transaction + span, transaction dropped

These match the hooks spec matrix exactly: before_send_log → before_send/log_item, before_send_metric → before_send/trace_metric. Note the spec forbids the callback_error reason here — "A dropped item MUST be reported exactly as it is on an explicit drop, and MUST NOT use a reason the callback could not otherwise produce" — so a throwing callback is deliberately indistinguishable from a deliberate drop.

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 in the spec matrix (it's the Action<SentryLog> overload parameter, already marked "will be removed in a future version"). It can't deliberately drop a log, so there's no "same report a deliberate drop would produce" to copy — before_send / log_item is the closest fit.
  • 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

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 87.50000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.96%. Comparing base (3546adc) to head (e25f203).

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.90%   74.96%   +0.06%     
==============================================================
  Files                             515      515              
  Lines                           18966    18993      +27     
  Branches                         3696     3698       +2     
==============================================================
+ Hits                            14206    14238      +32     
+ Misses                           3878     3874       -4     
+ Partials                          882      881       -1     

☔ 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

@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>

This branch has not been deployed

No deployments
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