@@ -164,10 +164,6 @@ def _make_span(
164164 - context is the span context
165165 - token is the context token needed for detaching
166166 """
167- # Log before we do anything
168- before_span = _get_current_span_info ()
169- logger .debug (f"[DEBUG] BEFORE _make_span { operation_name } .{ span_kind } - Current context: { before_span } " )
170-
171167 # Create span with proper naming convention
172168 span_name = f"{ operation_name } .{ span_kind } "
173169
@@ -186,20 +182,19 @@ def _make_span(
186182 if version is not None :
187183 attributes [SpanAttributes .OPERATION_VERSION ] = version
188184
189- # Get current context explicitly
190185 current_context = context_api .get_current ()
191186
192- # Create the span with explicit context
193- span = tracer .start_span (span_name , context = current_context , attributes = attributes )
194-
195- # Set as current context and get token for later detachment
187+ # Create the span with proper context management
188+ if span_kind == SpanKind .SESSION :
189+ # For session spans, create as a root span
190+ span = tracer .start_span (span_name , attributes = attributes )
191+ else :
192+ # For other spans, use the current context
193+ span = tracer .start_span (span_name , context = current_context , attributes = attributes )
194+
195+ # Set as current context and get token for detachment
196196 ctx = trace .set_span_in_context (span )
197197 token = context_api .attach (ctx )
198-
199- # Log after span creation
200- if hasattr (span , "get_span_context" ):
201- span_ctx = span .get_span_context ()
202- logger .debug (f"[DEBUG] CREATED _make_span { span_name } - span_id: { span_ctx .span_id :x} , parent: { before_span .get ('span_id' , 'None' )} " )
203198
204199 return span , ctx , token
205200
@@ -232,19 +227,49 @@ def _record_entity_output(span: trace.Span, result: Any) -> None:
232227
233228
234229def _finalize_span (span : trace .Span , token : Any ) -> None :
235- """End the span and detach the context token"""
236- if hasattr (span , "get_span_context" ) and hasattr (span .get_span_context (), "span_id" ):
237- span_id = f"{ span .get_span_context ().span_id :x} "
238- logger .debug (f"[DEBUG] ENDING span { getattr (span , 'name' , 'unknown' )} - span_id: { span_id } " )
230+ """
231+ Finalizes a span and cleans up its context.
239232
240- span .end ()
233+ This function performs three critical tasks needed for proper span lifecycle management:
234+ 1. Ends the span to mark it complete and calculate its duration
235+ 2. Detaches the context token to prevent memory leaks and maintain proper context hierarchy
236+ 3. Forces immediate span export rather than waiting for batch processing
237+
238+ Use cases:
239+ - Session span termination: Ensures root spans are properly ended and exported
240+ - Shutdown handling: Ensures spans are flushed during application termination
241+ - Async operations: Finalizes spans from asynchronous execution contexts
241242
242- # Debug info before detaching
243- current_after_end = _get_current_span_info ()
244- logger .debug (f"[DEBUG] AFTER span.end() - Current context: { current_after_end } " )
243+ Without proper finalization, spans may not trigger on_end events in processors,
244+ potentially resulting in missing or incomplete telemetry data.
245245
246- context_api .detach (token )
246+ Args:
247+ span: The span to finalize
248+ token: The context token to detach
249+ """
250+ # End the span
251+ if span :
252+ try :
253+ span .end ()
254+ except Exception as e :
255+ logger .warning (f"Error ending span: { e } " )
256+
257+ # Detach context token if provided
258+ if token :
259+ try :
260+ context_api .detach (token )
261+ except Exception :
262+ pass
247263
248- # Debug info after detaching
249- final_context = _get_current_span_info ()
250- logger .debug (f"[DEBUG] AFTER detach - Final context: { final_context } " )
264+ # Try to flush span processors
265+ # Note: force_flush() might not be available in certain scenarios:
266+ # - During application shutdown when the provider may be partially destroyed
267+ # We use try/except to gracefully handle these cases while ensuring spans are
268+ # flushed when possible, which is especially critical for session spans.
269+ try :
270+ from opentelemetry .trace import get_tracer_provider
271+ tracer_provider = get_tracer_provider ()
272+ tracer_provider .force_flush ()
273+ except (AttributeError , Exception ):
274+ # Either force_flush doesn't exist or there was an error calling it
275+ pass
0 commit comments