Skip to content

Commit c1ab8d2

Browse files
committed
_record_exception_on_span
1 parent 7367ed5 commit c1ab8d2

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

litellm/integrations/opentelemetry.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,10 @@ def _handle_failure(self, kwargs, response_obj, start_time, end_time):
789789
)
790790
span.set_status(Status(StatusCode.ERROR))
791791
self.set_attributes(span, kwargs, response_obj)
792+
793+
# Record exception information using OTEL standard method
794+
self._record_exception_on_span(span=span, kwargs=kwargs)
795+
792796
span.end(end_time=self._to_ns(end_time))
793797

794798
# Create span for guardrail information
@@ -797,6 +801,87 @@ def _handle_failure(self, kwargs, response_obj, start_time, end_time):
797801
if parent_otel_span is not None:
798802
parent_otel_span.end(end_time=self._to_ns(datetime.now()))
799803

804+
def _record_exception_on_span(self, span: Span, kwargs: dict):
805+
"""
806+
Record exception information on the span using OTEL standard methods.
807+
808+
This extracts error information from StandardLoggingPayload and:
809+
1. Uses span.record_exception() for the actual exception object (OTEL standard)
810+
2. Sets structured error attributes from StandardLoggingPayloadErrorInformation
811+
"""
812+
try:
813+
from litellm.integrations._types.open_inference import ErrorAttributes
814+
815+
# Get the exception object if available
816+
exception = kwargs.get("exception")
817+
818+
# Record the exception using OTEL's standard method
819+
if exception is not None:
820+
span.record_exception(exception)
821+
822+
# Get StandardLoggingPayload for structured error information
823+
standard_logging_payload: Optional[StandardLoggingPayload] = kwargs.get(
824+
"standard_logging_object"
825+
)
826+
827+
if standard_logging_payload is None:
828+
return
829+
830+
# Extract error_information from StandardLoggingPayload
831+
error_information = standard_logging_payload.get("error_information")
832+
833+
if error_information is None:
834+
# Fallback to error_str if error_information is not available
835+
error_str = standard_logging_payload.get("error_str")
836+
if error_str:
837+
self.safe_set_attribute(
838+
span=span,
839+
key=ErrorAttributes.ERROR_MESSAGE,
840+
value=error_str,
841+
)
842+
return
843+
844+
# Set structured error attributes from StandardLoggingPayloadErrorInformation
845+
if error_information.get("error_code"):
846+
self.safe_set_attribute(
847+
span=span,
848+
key=ErrorAttributes.ERROR_CODE,
849+
value=error_information["error_code"],
850+
)
851+
852+
if error_information.get("error_class"):
853+
self.safe_set_attribute(
854+
span=span,
855+
key=ErrorAttributes.ERROR_TYPE,
856+
value=error_information["error_class"],
857+
)
858+
859+
if error_information.get("error_message"):
860+
self.safe_set_attribute(
861+
span=span,
862+
key=ErrorAttributes.ERROR_MESSAGE,
863+
value=error_information["error_message"],
864+
)
865+
866+
if error_information.get("llm_provider"):
867+
self.safe_set_attribute(
868+
span=span,
869+
key=ErrorAttributes.ERROR_LLM_PROVIDER,
870+
value=error_information["llm_provider"],
871+
)
872+
873+
if error_information.get("traceback"):
874+
self.safe_set_attribute(
875+
span=span,
876+
key=ErrorAttributes.ERROR_STACK_TRACE,
877+
value=error_information["traceback"],
878+
)
879+
880+
except Exception as e:
881+
verbose_logger.exception(
882+
"OpenTelemetry: Error recording exception on span: %s", str(e)
883+
)
884+
800885
def set_tools_attributes(self, span: Span, tools):
801886
import json
802887

0 commit comments

Comments
 (0)