33
44import sentry_sdk
55from sentry_sdk .ai .monitoring import set_ai_pipeline_name , record_token_usage
6- from sentry_sdk .consts import OP , SPANDATA
6+ from sentry_sdk .consts import OP , SPANDATA , SPANSTATUS
77from sentry_sdk .ai .utils import set_data_normalized
88from sentry_sdk .scope import should_send_default_pii
9- from sentry_sdk .tracing import Span
9+ from sentry_sdk .tracing import POTelSpan as Span
1010from sentry_sdk .integrations import DidNotEnable , Integration
1111from sentry_sdk .utils import logger , capture_internal_exceptions
1212
@@ -72,7 +72,6 @@ def setup_once():
7272
7373
7474class WatchedSpan :
75- span = None # type: Span
7675 num_completion_tokens = 0 # type: int
7776 num_prompt_tokens = 0 # type: int
7877 no_collect_tokens = False # type: bool
@@ -123,8 +122,9 @@ def _handle_error(self, run_id, error):
123122 span_data = self .span_map [run_id ]
124123 if not span_data :
125124 return
126- sentry_sdk .capture_exception (error , span_data .span .scope )
127- span_data .span .__exit__ (None , None , None )
125+ sentry_sdk .capture_exception (error )
126+ span_data .span .set_status (SPANSTATUS .INTERNAL_ERROR )
127+ span_data .span .finish ()
128128 del self .span_map [run_id ]
129129
130130 def _normalize_langchain_message (self , message ):
@@ -136,23 +136,27 @@ def _normalize_langchain_message(self, message):
136136 def _create_span (self , run_id , parent_id , ** kwargs ):
137137 # type: (SentryLangchainCallback, UUID, Optional[Any], Any) -> WatchedSpan
138138
139- watched_span = None # type: Optional[WatchedSpan]
140- if parent_id :
141- parent_span = self .span_map .get (parent_id ) # type: Optional[WatchedSpan]
142- if parent_span :
143- watched_span = WatchedSpan (parent_span .span .start_child (** kwargs ))
144- parent_span .children .append (watched_span )
145- if watched_span is None :
146- watched_span = WatchedSpan (
147- sentry_sdk .start_span (only_if_parent = True , ** kwargs )
148- )
139+ parent_watched_span = self .span_map .get (parent_id ) if parent_id else None
140+ sentry_span = sentry_sdk .start_span (
141+ parent_span = parent_watched_span .span if parent_watched_span else None ,
142+ only_if_parent = True ,
143+ ** kwargs ,
144+ )
145+ watched_span = WatchedSpan (sentry_span )
146+ if parent_watched_span :
147+ parent_watched_span .children .append (watched_span )
149148
150149 if kwargs .get ("op" , "" ).startswith ("ai.pipeline." ):
151150 if kwargs .get ("name" ):
152151 set_ai_pipeline_name (kwargs .get ("name" ))
153152 watched_span .is_pipeline = True
154153
155- watched_span .span .__enter__ ()
154+ # the same run_id is reused for the pipeline it seems
155+ # so we need to end the older span to avoid orphan spans
156+ existing_span_data = self .span_map .get (run_id )
157+ if existing_span_data is not None :
158+ self ._exit_span (existing_span_data , run_id )
159+
156160 self .span_map [run_id ] = watched_span
157161 self .gc_span_map ()
158162 return watched_span
@@ -163,7 +167,8 @@ def _exit_span(self, span_data, run_id):
163167 if span_data .is_pipeline :
164168 set_ai_pipeline_name (None )
165169
166- span_data .span .__exit__ (None , None , None )
170+ span_data .span .set_status (SPANSTATUS .OK )
171+ span_data .span .finish ()
167172 del self .span_map [run_id ]
168173
169174 def on_llm_start (
0 commit comments