Spaces:
Sleeping
Sleeping
Upload __init__.py
Browse files- aworld/trace/__init__.py +135 -0
aworld/trace/__init__.py
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# coding: utf-8
|
2 |
+
# Copyright (c) 2025 inclusionAI.
|
3 |
+
import typing
|
4 |
+
from os import linesep
|
5 |
+
from aworld.trace.base import Span
|
6 |
+
from aworld.trace.span_cosumer import SpanConsumer, get_span_consumers
|
7 |
+
from opentelemetry.sdk.trace import ReadableSpan
|
8 |
+
from opentelemetry.sdk.trace.export import SpanExportResult, SpanExporter
|
9 |
+
from aworld.logs.util import logger
|
10 |
+
|
11 |
+
|
12 |
+
class FileSpanExporter(SpanExporter):
|
13 |
+
"""Implementation of :class:`SpanExporter` that prints spans to the
|
14 |
+
console.
|
15 |
+
|
16 |
+
This class can be used for diagnostic purposes. It prints the exported
|
17 |
+
spans to the console STDOUT.
|
18 |
+
"""
|
19 |
+
|
20 |
+
def __init__(
|
21 |
+
self,
|
22 |
+
file_path: str = None,
|
23 |
+
formatter: typing.Callable[
|
24 |
+
[ReadableSpan], str
|
25 |
+
] = lambda span: span.to_json() + linesep,
|
26 |
+
):
|
27 |
+
self.formatter = formatter
|
28 |
+
self.file_path = file_path
|
29 |
+
|
30 |
+
def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
|
31 |
+
try:
|
32 |
+
with open(self.file_path, 'a') as f:
|
33 |
+
for span in spans:
|
34 |
+
f.write(self.formatter(span))
|
35 |
+
|
36 |
+
return SpanExportResult.SUCCESS
|
37 |
+
except Exception as e:
|
38 |
+
logger.error(e)
|
39 |
+
return SpanExportResult.FAILURE
|
40 |
+
|
41 |
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
42 |
+
return True
|
43 |
+
|
44 |
+
|
45 |
+
class ReadOnlySpan(Span, ReadableSpan):
|
46 |
+
"""Implementation of :class:`Span` that wraps a :class:`ReadableSpan`.
|
47 |
+
This class can be used to wrap a :class:`ReadableSpan` to make it
|
48 |
+
read-only.
|
49 |
+
Args:
|
50 |
+
span: The span to wrap.
|
51 |
+
"""
|
52 |
+
|
53 |
+
def __init__(self, span: ReadableSpan):
|
54 |
+
self._span = span
|
55 |
+
|
56 |
+
if not typing.TYPE_CHECKING:
|
57 |
+
def __getattr__(self, name: str) -> typing.Any:
|
58 |
+
return getattr(self._span, name)
|
59 |
+
|
60 |
+
def end(self, end_time: typing.Optional[int] = None) -> None:
|
61 |
+
pass
|
62 |
+
|
63 |
+
def set_attribute(self, key: str, value: typing.Any) -> None:
|
64 |
+
pass
|
65 |
+
|
66 |
+
def set_attributes(self, attributes: dict[str, typing.Any]) -> None:
|
67 |
+
pass
|
68 |
+
|
69 |
+
def is_recording(self) -> bool:
|
70 |
+
return False
|
71 |
+
|
72 |
+
def record_exception(
|
73 |
+
self,
|
74 |
+
exception: BaseException,
|
75 |
+
attributes: dict[str, typing.Any] = None,
|
76 |
+
timestamp: typing.Optional[int] = None,
|
77 |
+
escaped: bool = False,
|
78 |
+
) -> None:
|
79 |
+
pass
|
80 |
+
|
81 |
+
def get_trace_id(self) -> str:
|
82 |
+
return f"{self._span.get_span_context().trace_id:032x}"
|
83 |
+
|
84 |
+
def get_span_id(self) -> str:
|
85 |
+
return f"{self._span.get_span_context().span_id:016x}"
|
86 |
+
|
87 |
+
|
88 |
+
class SpanConsumerExporter(SpanExporter):
|
89 |
+
"""Implementation of :class:`SpanExporter` that exports spans to
|
90 |
+
multiple span consumers.
|
91 |
+
This class can be used for exporting spans to multiple span consumers.
|
92 |
+
It exports the spans to the span consumers in the order they are passed
|
93 |
+
in the constructor.
|
94 |
+
Args:
|
95 |
+
span_consumers: A sequence of span consumers to export spans to.
|
96 |
+
"""
|
97 |
+
|
98 |
+
def __init__(
|
99 |
+
self,
|
100 |
+
span_consumers: typing.Sequence[SpanConsumer] = None,
|
101 |
+
):
|
102 |
+
self._span_consumers = span_consumers or []
|
103 |
+
self._loaded = False
|
104 |
+
|
105 |
+
def _load_span_consumers(self):
|
106 |
+
if not self._loaded:
|
107 |
+
self._span_consumers.extend(get_span_consumers())
|
108 |
+
self._loaded = True
|
109 |
+
|
110 |
+
def export(
|
111 |
+
self, spans: typing.Sequence[ReadableSpan]
|
112 |
+
) -> SpanExportResult:
|
113 |
+
self._load_span_consumers()
|
114 |
+
span_batches = []
|
115 |
+
for span in spans:
|
116 |
+
span_batches.append(ReadOnlySpan(span))
|
117 |
+
for span_consumer in self._span_consumers:
|
118 |
+
try:
|
119 |
+
span_consumer.consume(span_batches)
|
120 |
+
except Exception as e:
|
121 |
+
logger.error(
|
122 |
+
f"Error consume spans: {e}, span_consumer: {span_consumer.__class__.__name__}")
|
123 |
+
return SpanExportResult.SUCCESS
|
124 |
+
|
125 |
+
|
126 |
+
class NoOpSpanExporter(SpanExporter):
|
127 |
+
"""Implementation of :class:`SpanExporter` that does not export spans."""
|
128 |
+
|
129 |
+
def export(
|
130 |
+
self, spans: typing.Sequence[ReadableSpan]
|
131 |
+
) -> SpanExportResult:
|
132 |
+
return SpanExportResult.SUCCESS
|
133 |
+
|
134 |
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
135 |
+
return True
|