1313
1414if TYPE_CHECKING :
1515 from typing import Any , Callable , Optional
16- from sentry_sdk .traces import StreamedSpan
16+ from sentry_sdk ._types import SpanJSON
1717
1818
19- class SpanBatcher (Batcher ["StreamedSpan " ]):
19+ class SpanBatcher (Batcher ["SpanJSON " ]):
2020 # MAX_BEFORE_FLUSH should be lower than MAX_BEFORE_DROP, so that there is
2121 # a bit of a buffer for spans that appear between the trigger to flush
2222 # and actually flushing the buffer.
@@ -42,7 +42,7 @@ def __init__(
4242 # by trace_id, so that we can then send the buckets each in its own
4343 # envelope.
4444 # trace_id -> span buffer
45- self ._span_buffer : dict [str , list ["StreamedSpan " ]] = defaultdict (list )
45+ self ._span_buffer : dict [str , list ["SpanJSON " ]] = defaultdict (list )
4646 self ._running_size : dict [str , int ] = defaultdict (lambda : 0 )
4747 self ._capture_func = capture_func
4848 self ._record_lost_func = record_lost_func
@@ -99,7 +99,7 @@ def _flush_loop(self) -> None:
9999 self ._flush ()
100100 self ._last_full_flush = time .monotonic ()
101101
102- def add (self , span : "StreamedSpan " ) -> None :
102+ def add (self , span : "SpanJSON " ) -> None :
103103 # Bail out if the current thread is already executing batcher code.
104104 # This prevents deadlocks when code running inside the batcher (e.g.
105105 # _add_to_envelope during flush, or _flush_event.wait/set) triggers
@@ -115,7 +115,7 @@ def add(self, span: "StreamedSpan") -> None:
115115 return None
116116
117117 with self ._lock :
118- size = len (self ._span_buffer [span . trace_id ])
118+ size = len (self ._span_buffer [span [ " trace_id" ] ])
119119 if size >= self .MAX_BEFORE_DROP :
120120 self ._record_lost_func (
121121 reason = "queue_overflow" ,
@@ -124,14 +124,15 @@ def add(self, span: "StreamedSpan") -> None:
124124 )
125125 return None
126126
127- self ._span_buffer [span . trace_id ].append (span )
128- self ._running_size [span . trace_id ] += self ._estimate_size (span )
127+ self ._span_buffer [span [ " trace_id" ] ].append (span )
128+ self ._running_size [span [ " trace_id" ] ] += self ._estimate_size (span )
129129
130130 if (
131131 size + 1 >= self .MAX_BEFORE_FLUSH
132- or self ._running_size [span .trace_id ] >= self .MAX_BYTES_BEFORE_FLUSH
132+ or self ._running_size [span ["trace_id" ]]
133+ >= self .MAX_BYTES_BEFORE_FLUSH
133134 ):
134- self ._pending_flush .add (span . trace_id )
135+ self ._pending_flush .add (span [ " trace_id" ] )
135136 notify = True
136137 else :
137138 notify = False
@@ -142,12 +143,12 @@ def add(self, span: "StreamedSpan") -> None:
142143 self ._active .flag = False
143144
144145 @staticmethod
145- def _estimate_size (item : "StreamedSpan " ) -> int :
146+ def _estimate_size (item : "SpanJSON " ) -> int :
146147 # Rough estimate of serialized span size that's quick to compute.
147148 # 210 is the rough size of the payload without attributes, and then we
148149 # estimate the attributes separately.
149150 estimate = 210
150- for value in item . _attributes .values ():
151+ for value in item [ "attributes" ] .values ():
151152 estimate += 50
152153
153154 if isinstance (value , str ):
@@ -158,26 +159,15 @@ def _estimate_size(item: "StreamedSpan") -> int:
158159 return estimate
159160
160161 @staticmethod
161- def _to_transport_format (item : "StreamedSpan" ) -> "Any" :
162- res : "dict[str, Any]" = {
163- "trace_id" : item .trace_id ,
164- "span_id" : item .span_id ,
165- "name" : item ._name if item ._name is not None else "<unlabeled span>" ,
166- "status" : item ._status ,
167- "is_segment" : item ._is_segment (),
168- "start_timestamp" : item ._start_timestamp .timestamp (),
169- }
170-
171- if item ._end_timestamp :
172- res ["end_timestamp" ] = item ._end_timestamp .timestamp ()
173-
174- if item ._parent_span_id :
175- res ["parent_span_id" ] = item ._parent_span_id
176-
177- if item ._attributes :
162+ def _to_transport_format (item : "SpanJSON" ) -> "Any" :
163+ res = {k : v for k , v in item .items () if k not in ("_segment_span" ,)}
164+
165+ if item .get ("attributes" ):
178166 res ["attributes" ] = {
179- k : serialize_attribute (v ) for (k , v ) in item . _attributes .items ()
167+ k : serialize_attribute (v ) for (k , v ) in item [ "attributes" ] .items ()
180168 }
169+ else :
170+ del res ["attributes" ]
181171
182172 return res
183173
@@ -201,7 +191,7 @@ def _flush(self, only_pending: bool = False) -> None:
201191 if not spans :
202192 continue
203193
204- dsc = spans [0 ]._dynamic_sampling_context ()
194+ dsc = spans [0 ][ "_segment_span" ] ._dynamic_sampling_context ()
205195
206196 # Max per envelope is 1000, so if we happen to have more than
207197 # 1000 spans in one bucket, we'll need to separate them.
0 commit comments