Skip to content

Commit 9e885f8

Browse files
Move encoding of spans to opentelemetry-exporter-otlp-proto-common
1 parent 48cfab5 commit 9e885f8

File tree

5 files changed

+43
-307
lines changed

5 files changed

+43
-307
lines changed
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,18 @@
1313
# limitations under the License.
1414

1515
import logging
16-
from collections import abc
17-
from typing import Any, List, Optional, Sequence
18-
16+
from collections import defaultdict
17+
from typing import List, Optional, Sequence
18+
19+
from opentelemetry.exporter.otlp.proto.common import (
20+
_encode_trace_id,
21+
_encode_span_id,
22+
_encode_key_value,
23+
_encode_instrumentation_scope,
24+
)
1925
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
2026
ExportTraceServiceRequest as PB2ExportTraceServiceRequest,
2127
)
22-
from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue
23-
from opentelemetry.proto.common.v1.common_pb2 import (
24-
ArrayValue as PB2ArrayValue,
25-
)
26-
from opentelemetry.proto.common.v1.common_pb2 import (
27-
InstrumentationScope as PB2InstrumentationScope,
28-
)
2928
from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue
3029
from opentelemetry.proto.resource.v1.resource_pb2 import (
3130
Resource as PB2Resource,
@@ -38,10 +37,8 @@
3837
)
3938
from opentelemetry.proto.trace.v1.trace_pb2 import Span as PB2SPan
4039
from opentelemetry.proto.trace.v1.trace_pb2 import Status as PB2Status
41-
from opentelemetry.sdk.trace import Event
42-
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
40+
from opentelemetry.sdk.trace import Event, ReadableSpan
4341
from opentelemetry.sdk.trace import Resource
44-
from opentelemetry.sdk.trace import Span as SDKSpan
4542
from opentelemetry.trace import Link
4643
from opentelemetry.trace import SpanKind
4744
from opentelemetry.trace.span import SpanContext, TraceState, Status
@@ -59,20 +56,16 @@
5956
_logger = logging.getLogger(__name__)
6057

6158

62-
class _ProtobufEncoder:
63-
@classmethod
64-
def serialize(cls, sdk_spans: Sequence[SDKSpan]) -> str:
65-
return cls.encode(sdk_spans).SerializeToString()
66-
67-
@staticmethod
68-
def encode(sdk_spans: Sequence[SDKSpan]) -> PB2ExportTraceServiceRequest:
69-
return PB2ExportTraceServiceRequest(
70-
resource_spans=_encode_resource_spans(sdk_spans)
71-
)
59+
def encode_spans(
60+
sdk_spans: Sequence[ReadableSpan],
61+
) -> PB2ExportTraceServiceRequest:
62+
return PB2ExportTraceServiceRequest(
63+
resource_spans=_encode_resource_spans(sdk_spans)
64+
)
7265

7366

7467
def _encode_resource_spans(
75-
sdk_spans: Sequence[SDKSpan],
68+
sdk_spans: Sequence[ReadableSpan],
7669
) -> List[PB2ResourceSpans]:
7770
# We need to inspect the spans and group + structure them as:
7871
#
@@ -85,25 +78,14 @@ def _encode_resource_spans(
8578
#
8679
# Second loop encodes the data into Protobuf format.
8780
#
88-
sdk_resource_spans = {}
81+
sdk_resource_spans = defaultdict(lambda: defaultdict(list))
8982

9083
for sdk_span in sdk_spans:
9184
sdk_resource = sdk_span.resource
9285
sdk_instrumentation = sdk_span.instrumentation_scope or None
9386
pb2_span = _encode_span(sdk_span)
9487

95-
if sdk_resource not in sdk_resource_spans.keys():
96-
sdk_resource_spans[sdk_resource] = {
97-
sdk_instrumentation: [pb2_span]
98-
}
99-
elif (
100-
sdk_instrumentation not in sdk_resource_spans[sdk_resource].keys()
101-
):
102-
sdk_resource_spans[sdk_resource][sdk_instrumentation] = [pb2_span]
103-
else:
104-
sdk_resource_spans[sdk_resource][sdk_instrumentation].append(
105-
pb2_span
106-
)
88+
sdk_resource_spans[sdk_resource][sdk_instrumentation].append(pb2_span)
10789

10890
pb2_resource_spans = []
10991

@@ -126,7 +108,7 @@ def _encode_resource_spans(
126108
return pb2_resource_spans
127109

128110

129-
def _encode_span(sdk_span: SDKSpan) -> PB2SPan:
111+
def _encode_span(sdk_span: ReadableSpan) -> PB2SPan:
130112
span_context = sdk_span.get_span_context()
131113
return PB2SPan(
132114
trace_id=_encode_trace_id(span_context.trace_id),
@@ -141,6 +123,9 @@ def _encode_span(sdk_span: SDKSpan) -> PB2SPan:
141123
events=_encode_events(sdk_span.events),
142124
links=_encode_links(sdk_span.links),
143125
status=_encode_status(sdk_span.status),
126+
dropped_attributes_count=sdk_span.dropped_attributes,
127+
dropped_events_count=sdk_span.dropped_events,
128+
dropped_links_count=sdk_span.dropped_links,
144129
)
145130

146131

@@ -154,6 +139,7 @@ def _encode_events(
154139
encoded_event = PB2SPan.Event(
155140
name=event.name,
156141
time_unix_nano=event.timestamp,
142+
dropped_attributes_count=event.attributes.dropped,
157143
)
158144
for key, value in event.attributes.items():
159145
try:
@@ -167,14 +153,15 @@ def _encode_events(
167153
return pb2_events
168154

169155

170-
def _encode_links(links: List[Link]) -> List[PB2SPan.Link]:
156+
def _encode_links(links: Sequence[Link]) -> Sequence[PB2SPan.Link]:
171157
pb2_links = None
172158
if links:
173159
pb2_links = []
174160
for link in links:
175161
encoded_link = PB2SPan.Link(
176162
trace_id=_encode_trace_id(link.context.trace_id),
177163
span_id=_encode_span_id(link.context.span_id),
164+
dropped_attributes_count=link.attributes.dropped,
178165
)
179166
for key, value in link.attributes.items():
180167
try:
@@ -208,11 +195,9 @@ def _encode_trace_state(trace_state: TraceState) -> Optional[str]:
208195

209196

210197
def _encode_parent_id(context: Optional[SpanContext]) -> Optional[bytes]:
211-
if isinstance(context, SpanContext):
212-
encoded_parent_id = _encode_span_id(context.span_id)
213-
else:
214-
encoded_parent_id = None
215-
return encoded_parent_id
198+
if context:
199+
return _encode_span_id(context.span_id)
200+
return None
216201

217202

218203
def _encode_attributes(
@@ -239,50 +224,3 @@ def _encode_resource(resource: Resource) -> PB2Resource:
239224
except Exception as error: # pylint: disable=broad-except
240225
_logger.exception(error)
241226
return pb2_resource
242-
243-
244-
def _encode_instrumentation_scope(
245-
instrumentation_scope: InstrumentationScope,
246-
) -> PB2InstrumentationScope:
247-
if instrumentation_scope is None:
248-
pb2_instrumentation_scope = PB2InstrumentationScope()
249-
else:
250-
pb2_instrumentation_scope = PB2InstrumentationScope(
251-
name=instrumentation_scope.name,
252-
version=instrumentation_scope.version,
253-
)
254-
return pb2_instrumentation_scope
255-
256-
257-
def _encode_value(value: Any) -> PB2AnyValue:
258-
if isinstance(value, bool):
259-
any_value = PB2AnyValue(bool_value=value)
260-
elif isinstance(value, str):
261-
any_value = PB2AnyValue(string_value=value)
262-
elif isinstance(value, int):
263-
any_value = PB2AnyValue(int_value=value)
264-
elif isinstance(value, float):
265-
any_value = PB2AnyValue(double_value=value)
266-
elif isinstance(value, abc.Sequence):
267-
any_value = PB2AnyValue(
268-
array_value=PB2ArrayValue(values=[_encode_value(v) for v in value])
269-
)
270-
# tracing specs currently does not support Mapping type attributes.
271-
# elif isinstance(value, abc.Mapping):
272-
# pass
273-
else:
274-
raise Exception(f"Invalid type {type(value)} of value {value}")
275-
return any_value
276-
277-
278-
def _encode_key_value(key: str, value: Any) -> PB2KeyValue:
279-
any_value = _encode_value(value)
280-
return PB2KeyValue(key=key, value=any_value)
281-
282-
283-
def _encode_span_id(span_id: int) -> bytes:
284-
return span_id.to_bytes(length=8, byteorder="big", signed=False)
285-
286-
287-
def _encode_trace_id(trace_id: int) -> bytes:
288-
return trace_id.to_bytes(length=16, byteorder="big", signed=False)

exporter/opentelemetry-exporter-otlp-proto-http/tests/test_protobuf_encoder.py renamed to exporter/opentelemetry-exporter-otlp-proto-common/tests/test_trace_encoder.py

+9-16
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
import unittest
1818
from typing import List, Tuple
1919

20-
from opentelemetry.exporter.otlp.proto.http.trace_exporter.encoder import (
21-
_SPAN_KIND_MAP,
20+
from opentelemetry.exporter.otlp.proto.common import (
2221
_encode_span_id,
23-
_encode_status,
2422
_encode_trace_id,
25-
_ProtobufEncoder,
23+
)
24+
from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
25+
_SPAN_KIND_MAP,
26+
_encode_status,
27+
encode_spans,
2628
)
2729
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
2830
ExportTraceServiceRequest as PB2ExportTraceServiceRequest,
@@ -55,19 +57,10 @@
5557
from opentelemetry.trace.status import StatusCode as SDKStatusCode
5658

5759

58-
class TestProtobufEncoder(unittest.TestCase):
59-
def test_encode(self):
60+
class TestOTLPTraceEncoder(unittest.TestCase):
61+
def test_encode_spans(self):
6062
otel_spans, expected_encoding = self.get_exhaustive_test_spans()
61-
self.assertEqual(
62-
_ProtobufEncoder().encode(otel_spans), expected_encoding
63-
)
64-
65-
def test_serialize(self):
66-
otel_spans, expected_encoding = self.get_exhaustive_test_spans()
67-
self.assertEqual(
68-
_ProtobufEncoder().serialize(otel_spans),
69-
expected_encoding.SerializeToString(),
70-
)
63+
self.assertEqual(encode_spans(otel_spans), expected_encoding)
7164

7265
@staticmethod
7366
def get_exhaustive_otel_span_list() -> List[SDKSpan]:

0 commit comments

Comments
 (0)