diff --git a/src/uipath/tracing/_otel_exporters.py b/src/uipath/tracing/_otel_exporters.py index 47c58738d..2dff61e8a 100644 --- a/src/uipath/tracing/_otel_exporters.py +++ b/src/uipath/tracing/_otel_exporters.py @@ -355,16 +355,10 @@ def __init__(self, file_path: str): def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: try: - uipath_spans = [ - _SpanUtils.otel_span_to_uipath_span( - span, serialize_attributes=True - ).to_dict(serialize_attributes=True) - for span in spans - ] - with open(self.file_path, "a") as f: - for span in uipath_spans: - f.write(json.dumps(span) + "\n") + for span in spans: + span_dict = _SpanUtils.readable_span_to_dict(span) + f.write(json.dumps(span_dict) + "\n") return SpanExportResult.SUCCESS except Exception as e: logger.error(f"Failed to export spans to {self.file_path}: {e}") diff --git a/src/uipath/tracing/_utils.py b/src/uipath/tracing/_utils.py index 09a3b6b7f..5b0faeacf 100644 --- a/src/uipath/tracing/_utils.py +++ b/src/uipath/tracing/_utils.py @@ -383,6 +383,56 @@ def _has_ancestor_with_name( return False + @staticmethod + def readable_span_to_dict(span: ReadableSpan) -> Dict[str, Any]: + """Convert a ReadableSpan to a dictionary for direct serialization. + + This method preserves the raw OpenTelemetry span structure without + UiPath-specific transformations. + + Args: + span: The OpenTelemetry ReadableSpan to convert + + Returns: + A dictionary representation of the span + """ + return { + "name": span.name, + "context": { + "trace_id": format(span.context.trace_id, "032x"), + "span_id": format(span.context.span_id, "016x"), + "trace_state": dict(span.context.trace_state) if span.context.trace_state else {}, + }, + "kind": str(span.kind), + "parent_id": format(span.parent.span_id, "016x") if span.parent else None, + "start_time": span.start_time, + "end_time": span.end_time, + "status": { + "status_code": str(span.status.status_code), + "description": span.status.description, + }, + "attributes": dict(span.attributes) if span.attributes else {}, + "events": [ + { + "name": event.name, + "timestamp": event.timestamp, + "attributes": dict(event.attributes) if event.attributes else {}, + } + for event in span.events + ], + "links": [ + { + "context": { + "trace_id": format(link.context.trace_id, "032x"), + "span_id": format(link.context.span_id, "016x"), + }, + "attributes": dict(link.attributes) if link.attributes else {}, + } + for link in span.links + ], + "resource": dict(span.resource.attributes) if span.resource else {}, + } + @staticmethod def spans_to_llm_context(spans: list[ReadableSpan]) -> str: """Convert spans to a formatted conversation history string suitable for LLM context.