Filter benign unobserved-Task socket noise from Sentry (BL-16573)#8080
Filter benign unobserved-Task socket noise from Sentry (BL-16573)#8080hatton wants to merge 4 commits into
Conversation
The Sentry .NET SDK auto-subscribes to TaskScheduler.UnobservedTaskException. Fleck (our WebSocket library, see BloomWebSocketServer) does fire-and-forget socket.Send() calls whose faulted Tasks are never observed; when a socket aborts (mostly at app shutdown) the finalizer thread surfaces a benign SocketException/IOException and Sentry reports it. An earlier IsAvailable-check mitigation (BL-11124) did not stop it, so ~71k events (0 users, 90d) piled up across three locale-split Sentry issues: BLOOM-DESKTOP-EQ4 / -E4J / -E9K. Switch SentrySdk.Init to the Action<SentryOptions> overload (same DSN) and add a narrowly-scoped BeforeSend filter that drops ONLY this pattern: an event carrying the UnobservedTaskException mechanism whose exception chain contains a benign socket/IO abort type. The decision is carried by exception TYPE plus the mechanism, never by message text, so it is locale-independent and covers all three issues (and future language variants) at once. Genuine unobserved-Task bugs are still reported; we deliberately do NOT disable the integration. The filter predicate is a testable internal static method with unit tests in BloomTests exercising matching (dropped), the localized-message variant, and non-matching (passed through) events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
| Filename | Overview |
|---|---|
| src/BloomExe/Program.cs | Adds two well-commented helper methods for Sentry noise filtering and switches SentrySdk.Init to the options overload with a narrowly-scoped BeforeSend filter; logic is correct and safe-fails on unexpected shapes |
| src/BloomTests/ProgramTests.cs | Adds 8 parameterized and standalone NUnit tests covering all four benign-type branches (drop), mixed chains with real bugs (keep), vacuous-truth guard, mechanism-absent case, and the empty-event edge case |
Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/mas..." | Re-trigger Greptile
…(BL-16573) Greptile P2 feedback on the Sentry unobserved-task filter: - Add coverage for the OperationCanceledException / TaskCanceledException branches of IsBenignSocketAbortExceptionType (they had no test). - Explain in a code comment why IOException and the cancellation types are screened alongside SocketException, and that the trade-off (an unobserved-Task IOException/cancellation is treated as noise) is deliberate. The predicate only runs for events already carrying the UnobservedTaskException mechanism, so it never suppresses these types on a normal observed path. No behavior change; the type set is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
[Claude Fable 5] Consulted Devin on 2026-07-18 19:22 up to commit c81bb65 (re-review after the review-follow-up commit). Result: 0 bugs, 1 Investigate, 1 Informational — the same AggregateException-unwrapping Investigate flag as the prior review, which is already posted as an inline thread, refuted with a real captured event, and resolved; skipped re-posting per dedup. Greptile re-review passed with no new findings; both earlier Greptile P2 items are addressed and resolved. CI green. Re-review clean — bots quiet. |
…L-16573) The unobserved-Task filter previously dropped an event if ANY exception in the chain was a benign socket-abort type, which could suppress a real bug aggregated alongside socket noise. Now at least one benign abort must be present AND every exception in the chain must be benign (or the AggregateException wrapper). Also documents why the mechanism check must remain Any. Adds tests for the mixed-chain and bare-wrapper cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bserved-socket-noise
|
[Claude Fable 5] Consulted Devin on 2026-07-23 up to commit d39376a (re-review after tightening the benign-type check from Any to All). Result: 0 bugs, 1 Investigate, 2 Informational. The Investigate flag is the same AggregateException-unwrapping question raised on the previous commit — its thread already has the answer (verified against a real captured event on BLOOM-DESKTOP-EQ4: the wrapper and its mechanism survive to BeforeSend in the SDK version we ship) and is resolved. The two Informational notes restate trade-offs already documented in code comments (the breadth of the benign type set, and BeforeSend applying to the whole pipeline but being scoped by the mechanism check). No new findings to mirror. |
JohnThomson
left a comment
There was a problem hiding this comment.
@JohnThomson+AGNT resolved 1 discussion.
Reviewable status: 0 of 2 files reviewed, all discussions resolved (waiting on hatton).
[Claude Fable 5]
Screens out the benign "unobserved Task socket/IO abort" noise that the Sentry .NET SDK reports on our behalf. Three locale-split Sentry issues share one root cause and together account for ~71,000 events / 0 users over 90 days:
Root cause
The Sentry SDK auto-subscribes to
TaskScheduler.UnobservedTaskException. Fleck (our WebSocket library, behindBloomWebSocketServer) does fire-and-forgetsocket.Send()calls whose faulted Tasks are never observed; when a socket aborts (mostly at app shutdown) the finalizer thread surfaces a benignSocketException/IOExceptionand Sentry reports it. As Sentry serializes it, the event is an innerSystem.Net.Sockets.SocketException("The I/O operation has been aborted...") wrapped in aSystem.AggregateExceptionwhose mechanism isUnobservedTaskException. An earlierIsAvailable-check mitigation (BL-11124) did not stop it.Fix
Switch
SentrySdk.Initto theAction<SentryOptions>overload (same DSN) and add a narrowly-scopedBeforeSendfilter that drops ONLY this pattern: an event carrying theUnobservedTaskExceptionmechanism whose exception chain contains a benign socket/IO abort type. The decision is carried by exception type + mechanism, never by message text, so it is locale-independent and covers all three issues (and future language variants) at once.Genuine unobserved-Task bugs are still reported - we deliberately do NOT call
DisableTaskUnobservedTaskExceptionCapture(). The filter predicate is a testableinternal staticmethod with unit tests inBloomTestscovering a matching event (dropped), the localized-message (Spanish) variant, and non-matching events (passed through).Ref: https://issues.bloomlibrary.org/youtrack/issue/BL-16573
🤖 Generated with Claude Code
Devin review
This change is