66
77import  os 
88import  threading 
9+ from  collections .abc  import  Mapping , Sequence 
910from  datetime  import  datetime 
1011from  enum  import  Enum 
1112from  typing  import  (
1213    Annotated ,
1314    Any ,
1415    Literal ,
16+     cast ,
1517)
1618
1719from  opentelemetry  import  metrics , trace 
3032
3133ROOT_SPAN_MARKERS  =  ["__root__" , "__root_span__" ]
3234
35+ # Type alias for OpenTelemetry attribute values (excludes None) 
36+ AttributeValue  =  str  |  bool  |  int  |  float  |  Sequence [str ] |  Sequence [bool ] |  Sequence [int ] |  Sequence [float ]
37+ Attributes  =  Mapping [str , AttributeValue ]
38+ 
3339
3440@json_schema_type  
3541class  SpanStatus (Enum ):
@@ -428,6 +434,13 @@ class QueryMetricsResponse(BaseModel):
428434logger  =  get_logger (name = __name__ , category = "telemetry" )
429435
430436
437+ def  _clean_attributes (attrs : dict [str , Any ] |  None ) ->  Attributes  |  None :
438+     """Remove None values from attributes dict to match OpenTelemetry's expected type.""" 
439+     if  attrs  is  None :
440+         return  None 
441+     return  {k : v  for  k , v  in  attrs .items () if  v  is  not None }
442+ 
443+ 
431444def  is_tracing_enabled (tracer ):
432445    with  tracer .start_as_current_span ("check_tracing" ) as  span :
433446        return  span .is_recording ()
@@ -456,7 +469,7 @@ def __init__(self) -> None:
456469                # https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter 
457470                span_exporter  =  OTLPSpanExporter ()
458471                span_processor  =  BatchSpanProcessor (span_exporter )
459-                 trace .get_tracer_provider ().add_span_processor (span_processor )
472+                 cast ( TracerProvider ,  trace .get_tracer_provider () ).add_span_processor (span_processor )
460473
461474                metric_reader  =  PeriodicExportingMetricReader (OTLPMetricExporter ())
462475                metric_provider  =  MeterProvider (metric_readers = [metric_reader ])
@@ -474,7 +487,7 @@ async def initialize(self) -> None:
474487
475488    async  def  shutdown (self ) ->  None :
476489        if  self .is_otel_endpoint_set :
477-             trace .get_tracer_provider ().force_flush ()
490+             cast ( TracerProvider ,  trace .get_tracer_provider () ).force_flush ()
478491
479492    async  def  log_event (self , event : Event , ttl_seconds : int  =  604800 ) ->  None :
480493        if  isinstance (event , UnstructuredLogEvent ):
@@ -515,7 +528,7 @@ def _get_or_create_counter(self, name: str, unit: str) -> metrics.Counter:
515528                unit = unit ,
516529                description = f"Counter for { name }  ,
517530            )
518-         return  _GLOBAL_STORAGE ["counters" ][name ]
531+         return  cast ( metrics . Counter ,  _GLOBAL_STORAGE ["counters" ][name ]) 
519532
520533    def  _get_or_create_gauge (self , name : str , unit : str ) ->  metrics .ObservableGauge :
521534        assert  self .meter  is  not None 
@@ -525,7 +538,7 @@ def _get_or_create_gauge(self, name: str, unit: str) -> metrics.ObservableGauge:
525538                unit = unit ,
526539                description = f"Gauge for { name }  ,
527540            )
528-         return  _GLOBAL_STORAGE ["gauges" ][name ]
541+         return  cast ( metrics . ObservableGauge ,  _GLOBAL_STORAGE ["gauges" ][name ]) 
529542
530543    def  _log_metric (self , event : MetricEvent ) ->  None :
531544        # Add metric as an event to the current span 
@@ -560,10 +573,10 @@ def _log_metric(self, event: MetricEvent) -> None:
560573            return 
561574        if  isinstance (event .value , int ):
562575            counter  =  self ._get_or_create_counter (event .metric , event .unit )
563-             counter .add (event .value , attributes = event .attributes )
576+             counter .add (event .value , attributes = _clean_attributes ( event .attributes ) )
564577        elif  isinstance (event .value , float ):
565578            up_down_counter  =  self ._get_or_create_up_down_counter (event .metric , event .unit )
566-             up_down_counter .add (event .value , attributes = event .attributes )
579+             up_down_counter .add (event .value , attributes = _clean_attributes ( event .attributes ) )
567580
568581    def  _get_or_create_up_down_counter (self , name : str , unit : str ) ->  metrics .UpDownCounter :
569582        assert  self .meter  is  not None 
@@ -573,7 +586,7 @@ def _get_or_create_up_down_counter(self, name: str, unit: str) -> metrics.UpDown
573586                unit = unit ,
574587                description = f"UpDownCounter for { name }  ,
575588            )
576-         return  _GLOBAL_STORAGE ["up_down_counters" ][name ]
589+         return  cast ( metrics . UpDownCounter ,  _GLOBAL_STORAGE ["up_down_counters" ][name ]) 
577590
578591    def  _log_structured (self , event : StructuredLogEvent , ttl_seconds : int ) ->  None :
579592        with  self ._lock :
@@ -601,7 +614,8 @@ def _log_structured(self, event: StructuredLogEvent, ttl_seconds: int) -> None:
601614                if  event .payload .parent_span_id :
602615                    parent_span_id  =  int (event .payload .parent_span_id , 16 )
603616                    parent_span  =  _GLOBAL_STORAGE ["active_spans" ].get (parent_span_id )
604-                     context  =  trace .set_span_in_context (parent_span )
617+                     if  parent_span :
618+                         context  =  trace .set_span_in_context (parent_span )
605619                elif  traceparent :
606620                    carrier  =  {
607621                        "traceparent" : traceparent ,
@@ -612,15 +626,17 @@ def _log_structured(self, event: StructuredLogEvent, ttl_seconds: int) -> None:
612626                span  =  tracer .start_span (
613627                    name = event .payload .name ,
614628                    context = context ,
615-                     attributes = event .attributes   or  {} ,
629+                     attributes = _clean_attributes ( event .attributes ) ,
616630                )
617631                _GLOBAL_STORAGE ["active_spans" ][span_id ] =  span 
618632
619633            elif  isinstance (event .payload , SpanEndPayload ):
620-                 span  =  _GLOBAL_STORAGE ["active_spans" ].get (span_id )
634+                 span  =  _GLOBAL_STORAGE ["active_spans" ].get (span_id )   # type: ignore[assignment] 
621635                if  span :
622636                    if  event .attributes :
623-                         span .set_attributes (event .attributes )
637+                         cleaned_attrs  =  _clean_attributes (event .attributes )
638+                         if  cleaned_attrs :
639+                             span .set_attributes (cleaned_attrs )
624640
625641                    status  =  (
626642                        trace .Status (status_code = trace .StatusCode .OK )
0 commit comments