@@ -64,6 +64,31 @@ def __iter__(self):
6464 yield k [len (self .prefix ) :]
6565
6666
67+ class _SpanRecorder (object ):
68+ __slots__ = ("maxlen" , "finished_spans" , "open_span_count" )
69+
70+ def __init__ (self , maxlen ):
71+ # type: (int) -> None
72+ self .maxlen = maxlen
73+ self .open_span_count = 0 # type: int
74+ self .finished_spans = [] # type: List[Span]
75+
76+ def start_span (self , span ):
77+ # type: (Span) -> None
78+
79+ # This is just so that we don't run out of memory while recording a lot
80+ # of spans. At some point we just stop and flush out the start of the
81+ # trace tree (i.e. the first n spans with the smallest
82+ # start_timestamp).
83+ self .open_span_count += 1
84+ if self .open_span_count > self .maxlen :
85+ span ._span_recorder = None
86+
87+ def finish_span (self , span ):
88+ # type: (Span) -> None
89+ self .finished_spans .append (span )
90+
91+
6792class Span (object ):
6893 __slots__ = (
6994 "trace_id" ,
@@ -78,7 +103,7 @@ class Span(object):
78103 "timestamp" ,
79104 "_tags" ,
80105 "_data" ,
81- "_finished_spans " ,
106+ "_span_recorder " ,
82107 "hub" ,
83108 "_context_manager_state" ,
84109 )
@@ -107,16 +132,18 @@ def __init__(
107132 self .hub = hub
108133 self ._tags = {} # type: Dict[str, str]
109134 self ._data = {} # type: Dict[str, Any]
110- self ._finished_spans = None # type: Optional[List[Span]]
111135 self .start_timestamp = datetime .now ()
112136
113137 #: End timestamp of span
114138 self .timestamp = None # type: Optional[datetime]
115139
116- def init_finished_spans (self ):
117- # type: () -> None
118- if self ._finished_spans is None :
119- self ._finished_spans = []
140+ self ._span_recorder = None # type: Optional[_SpanRecorder]
141+
142+ def init_finished_spans (self , maxlen ):
143+ # type: (int) -> None
144+ if self ._span_recorder is None :
145+ self ._span_recorder = _SpanRecorder (maxlen )
146+ self ._span_recorder .start_span (self )
120147
121148 def __repr__ (self ):
122149 # type: () -> str
@@ -162,7 +189,8 @@ def new_span(self, **kwargs):
162189 sampled = self .sampled ,
163190 ** kwargs
164191 )
165- rv ._finished_spans = self ._finished_spans
192+
193+ rv ._span_recorder = self ._span_recorder
166194 return rv
167195
168196 @classmethod
@@ -252,11 +280,13 @@ def finish(self, hub=None):
252280
253281 self .timestamp = datetime .now ()
254282
255- if self ._finished_spans is not None :
256- self ._finished_spans .append (self )
257-
258283 _maybe_create_breadcrumbs_from_span (hub , self )
259284
285+ if self ._span_recorder is None :
286+ return None
287+
288+ self ._span_recorder .finish_span (self )
289+
260290 if self .transaction is None :
261291 # If this has no transaction set we assume there's a parent
262292 # transaction for this span that would be flushed out eventually.
@@ -285,7 +315,9 @@ def finish(self, hub=None):
285315 "timestamp" : self .timestamp ,
286316 "start_timestamp" : self .start_timestamp ,
287317 "spans" : [
288- s .to_json () for s in (self ._finished_spans or ()) if s is not self
318+ s .to_json ()
319+ for s in self ._span_recorder .finished_spans
320+ if s is not self
289321 ],
290322 }
291323 )
@@ -354,11 +386,19 @@ def record_sql_queries(
354386 executemany , # type: bool
355387):
356388 # type: (...) -> Generator[Span, None, None]
357- if not params_list or params_list == [None ]:
358- params_list = None
359389
360- if paramstyle == "pyformat" :
361- paramstyle = "format"
390+ # TODO: Bring back capturing of params by default
391+ if hub .client and hub .client .options ["_experiments" ].get (
392+ "record_sql_params" , False
393+ ):
394+ if not params_list or params_list == [None ]:
395+ params_list = None
396+
397+ if paramstyle == "pyformat" :
398+ paramstyle = "format"
399+ else :
400+ params_list = None
401+ paramstyle = None
362402
363403 query = _format_sql (cursor , query )
364404
0 commit comments