feat(span-first): Support before_send_span#6239
Conversation
before_send_span
Codecov Results 📊✅ 2276 passed | ❌ 3 failed | ⏭️ 261 skipped | Total: 2540 | Pass Rate: 89.61% | Execution Time: 6m 30s ❌ Failed Tests
|
| File | Patch % | Lines |
|---|---|---|
utils.py |
83.88% | |
client.py |
85.30% | |
traces.py |
87.23% | |
_span_batcher.py |
85.83% | |
_types.py |
76.19% | |
consts.py |
99.22% |
Generated by Codecov Action
Codecov Results 📊✅ 44 passed | ❌ 13 failed | Total: 57 | Pass Rate: 77.19% | Execution Time: 9.62s ❌ Failed Tests
|
| File | Patch % | Lines |
|---|---|---|
utils.py |
54.32% | |
client.py |
61.82% | |
consts.py |
99.22% |
Generated by Codecov Action
| # estimate the attributes separately. | ||
| estimate = 210 | ||
| for value in item._attributes.values(): | ||
| for value in item["attributes"].values(): |
There was a problem hiding this comment.
_estimate_size raises KeyError when span lacks attributes
_estimate_size accesses item["attributes"].values() unconditionally, but 'attributes' is NotRequired in SpanJSON. The new before_send_span feature allows users to return a modified dict. While the intent is that invalid returns should fall back to the original span, the validation at client.py:979 only checks isinstance(serialized, dict) and serialized (non-empty). This allows dicts like {"not_a_span": True} to pass validation. When such a dict reaches _span_batcher.add(), it will raise KeyError in _estimate_size at line 151, causing the span to be dropped and potentially propagating the exception.
Verification
Read client.py and confirmed before_send_span handling at lines 964-986. Line 979 validation only checks for non-empty dict, with a TODO comment at line 980 acknowledging missing validation. SpanJSON TypedDict in _types.py line 331 marks attributes as NotRequired. Test at test_span_streaming.py line 318 shows before_send_span can return {} or {"not_a_span": True}. Empty dict {} is caught by falsy check, but {"not_a_span": True} passes through. Confirmed _estimate_size in _span_batcher.py line 151 accesses attributes unconditionally. No defensive code exists between client.py validation and _estimate_size call.
Also found at 2 additional locations
sentry_sdk/_span_batcher.py:165-170sentry_sdk/client.py:978-986
Identified by Warden find-bugs · VPT-R9G
Description
Add support for
before_send_spanin span streaming mode.before_send_spanis different frombefore_send_metricandbefore_send_login that:None)To that end, we're exposing a copy of the span in the callback instead of the real thing. Alternatively, we could just expose a simplified dictionary snapshot of the span (
{"name": "name", "attributes": {}}), but that'd then bypass any extra logic we have in the span public API (likeset_attribute(), which currently stringifies values if not serializable).See https://develop.sentry.dev/sdk/telemetry/spans/scrubbing-data/ for spec.
TODO
Issues
before_send_span#5388