44This module contains processors for OpenTelemetry spans.
55"""
66
7- import copy
8- import threading
97import time
108from threading import Event , Lock , Thread
11- from typing import Any , Dict , List , Optional
9+ from typing import Dict , Optional
1210
1311from opentelemetry .context import Context
1412from opentelemetry .sdk .trace import ReadableSpan , Span , SpanProcessor
1513from opentelemetry .sdk .trace .export import SpanExporter
16- from termcolor import colored
1714
1815import agentops .semconv as semconv
1916from agentops .logging import logger
20- from agentops .sdk . converters import trace_id_to_uuid , uuid_to_int16
17+ from agentops .helpers . dashboard import log_trace_url
2118from agentops .semconv .core import CoreAttributes
2219
2320
@@ -89,14 +86,7 @@ class InternalSpanProcessor(SpanProcessor):
8986 For session spans, it prints a URL to the AgentOps dashboard.
9087 """
9188
92- def __init__ (self , app_url : str = "https://app.agentops.ai" ):
93- """
94- Initialize the PrintSpanProcessor.
95-
96- Args:
97- app_url: The base URL for the AgentOps dashboard.
98- """
99- self .app_url = app_url
89+ _root_span_id : Optional [Span ] = None
10090
10191 def on_start (self , span : Span , parent_context : Optional [Context ] = None ) -> None :
10292 """
@@ -110,29 +100,10 @@ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None
110100 if not span .context or not span .context .trace_flags .sampled :
111101 return
112102
113- # Get the span kind from attributes
114- span_kind = (
115- span .attributes .get (semconv .SpanAttributes .AGENTOPS_SPAN_KIND , "unknown" ) if span .attributes else "unknown"
116- )
117-
118- # Print basic information about the span
119- logger .debug (f"Started span: { span .name } (kind: { span_kind } )" )
120-
121- # Special handling for session spans
122- if span_kind == semconv .SpanKind .SESSION :
123- trace_id = span .context .trace_id
124- # Convert trace_id to hex string if it's not already
125- if isinstance (trace_id , int ):
126- session_url = f"{ self .app_url } /drilldown?session_id={ trace_id_to_uuid (trace_id )} "
127- logger .info (
128- colored (
129- f"\x1b [34mSession started: { session_url } \x1b [0m" ,
130- "light_green" ,
131- )
132- )
133- else :
134- # Print basic information for other span kinds
135- logger .debug (f"Ended span: { span .name } (kind: { span_kind } )" )
103+ if not self ._root_span_id :
104+ self ._root_span_id = span .context .span_id
105+ logger .debug (f"[agentops.InternalSpanProcessor] Found root span: { span .name } " )
106+ log_trace_url (span )
136107
137108 def on_end (self , span : ReadableSpan ) -> None :
138109 """
@@ -145,30 +116,13 @@ def on_end(self, span: ReadableSpan) -> None:
145116 if not span .context or not span .context .trace_flags .sampled :
146117 return
147118
148- # Get the span kind from attributes
149- span_kind = (
150- span .attributes .get (semconv .SpanAttributes .AGENTOPS_SPAN_KIND , "unknown" ) if span .attributes else "unknown"
151- )
152-
153- # Special handling for session spans
154- if span_kind == semconv .SpanKind .SESSION :
155- trace_id = span .context .trace_id
156- # Convert trace_id to hex string if it's not already
157- if isinstance (trace_id , int ):
158- session_url = f"{ self .app_url } /drilldown?session_id={ trace_id_to_uuid (trace_id )} "
159- logger .info (
160- colored (
161- f"\x1b [34mSession Replay: { session_url } \x1b [0m" ,
162- "blue" ,
163- )
164- )
165- else :
166- # Print basic information for other span kinds
167- logger .debug (f"Ended span: { span .name } (kind: { span_kind } )" )
119+ if self ._root_span_id and (span .context .span_id is self ._root_span_id ):
120+ logger .debug (f"[agentops.InternalSpanProcessor] Ending root span: { span .name } " )
121+ log_trace_url (span )
168122
169123 def shutdown (self ) -> None :
170124 """Shutdown the processor."""
171- pass
125+ self . _root_span_id = None
172126
173127 def force_flush (self , timeout_millis : int = 30000 ) -> bool :
174128 """Force flush the processor."""
0 commit comments