@@ -113,7 +113,11 @@ def limit_exception(exc_ident: int) -> bool:
113113def unwind_exception_chain (
114114 exc : t .Optional [BaseException ], tb : t .Optional [TracebackType ]
115115) -> t .Tuple [ExceptionChain , t .Optional [uuid .UUID ]]:
116- """Unwind the exception chain and assign it an ID."""
116+ """Unwind the exception chain and assign it an ID.
117+
118+ The chain goes from "cause to effect", meaning that every cause for an
119+ exception is put first.
120+ """
117121 chain : ExceptionChain = deque ()
118122
119123 while exc is not None :
@@ -141,9 +145,13 @@ def unwind_exception_chain(
141145 return chain , exc_id
142146
143147
144- def get_tb_frames_from_exception_chain (chain : ExceptionChain ) -> t .List [TracebackType ]:
145- """Get the frames from the exception chain."""
146- frames : t .List [TracebackType ] = []
148+ def get_tb_frames_from_exception_chain (chain : ExceptionChain ) -> t .Generator [t .Tuple [int , TracebackType ], None , None ]:
149+ """Get the frames from the exception chain.
150+
151+ For each exception in the chain we collect the maximum number of frames
152+ configured, starting from the point where the exception was thrown.
153+ """
154+ frame_count = 0
147155
148156 for _ , tb in chain :
149157 local_frames = []
@@ -157,9 +165,21 @@ def get_tb_frames_from_exception_chain(chain: ExceptionChain) -> t.List[Tracebac
157165
158166 # Get only the last N frames from the traceback, where N is the
159167 # configured max size for span tracebacks.
160- frames .extend (local_frames [- global_config ._span_traceback_max_size :])
168+ # local_frames = local_frames[-global_config._span_traceback_max_size :]
169+ local_frames [: - global_config ._span_traceback_max_size ] = []
170+
171+ # Update the frame count to allow computing the sequence number
172+ frame_count += len (local_frames )
173+
174+ # Set the initial sequence number. This will decrease as we yield the
175+ # new frames
176+ frame_index = frame_count
161177
162- return frames
178+ # Pop and yield the frames from this traceback in reverse order
179+ while local_frames :
180+ frame = local_frames .pop ()
181+ yield frame_index , frame
182+ frame_index -= 1
163183
164184
165185class SpanExceptionProbe (LogLineProbe ):
@@ -262,8 +282,7 @@ def _attach_tb_frame_snapshot_to_span(
262282 return False
263283
264284 snapshot = None
265- snapshot_id = frame .f_locals .get (SNAPSHOT_KEY , None )
266- if snapshot_id is None :
285+ if (snapshot_id := frame .f_locals .get (SNAPSHOT_KEY , None )) is None :
267286 # We don't have a snapshot for the frame so we create one
268287 if cached_only :
269288 # If we only want a cached snapshot we return True as a signal
@@ -328,7 +347,7 @@ def on_span_exception(
328347
329348 # Capture more frames if we have budget left, otherwise set just the
330349 # tags on the span for those frames that we have already captured.
331- for seq_nr , _tb in reversed ( list ( enumerate ( get_tb_frames_from_exception_chain (chain ), 1 )) ):
350+ for seq_nr , _tb in get_tb_frames_from_exception_chain (chain ):
332351 has_snapshot_budget = frames_captured < config .max_frames
333352 has_captured = self ._attach_tb_frame_snapshot_to_span (
334353 span , _tb , exc_id , seq_nr , cached_only = not has_snapshot_budget
0 commit comments