Skip to content

Commit f11ed2f

Browse files
authored
Fix OTLP exporter not grouping exported spans properly (#1927)
1 parent b572e11 commit f11ed2f

File tree

4 files changed

+251
-25
lines changed

4 files changed

+251
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
- Added `BoundedAttributes` to the API to make it available for `Link` which is defined in the
2727
API. Marked `BoundedDict` in the SDK as deprecated as a result.
2828
([#1915](https://github.com/open-telemetry/opentelemetry-python/pull/1915))
29+
- Fix OTLP SpanExporter to distinguish spans based off Resource and InstrumentationInfo
30+
([#1927](https://github.com/open-telemetry/opentelemetry-python/pull/1927))
2931

3032
### Fixed
3133
- Updated `opentelementry-opentracing-shim` `ScopeShim` to report exceptions in

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ def get_resource_data(
157157
resource_class(
158158
**{
159159
"resource": collector_resource,
160-
"instrumentation_library_{}".format(name): [
161-
instrumentation_library_data
162-
],
160+
"instrumentation_library_{}".format(
161+
name
162+
): instrumentation_library_data.values(),
163163
}
164164
)
165165
)

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/trace_exporter/__init__.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -238,29 +238,42 @@ def _translate_data(
238238
sdk_resource_instrumentation_library_spans = {}
239239

240240
for sdk_span in data:
241-
242-
if sdk_span.resource not in (
243-
sdk_resource_instrumentation_library_spans.keys()
244-
):
241+
instrumentation_library_spans_map = (
242+
sdk_resource_instrumentation_library_spans.get(
243+
sdk_span.resource, {}
244+
)
245+
)
246+
# If we haven't seen the Resource yet, add it to the map
247+
if not instrumentation_library_spans_map:
248+
sdk_resource_instrumentation_library_spans[
249+
sdk_span.resource
250+
] = instrumentation_library_spans_map
251+
instrumentation_library_spans = (
252+
instrumentation_library_spans_map.get(
253+
sdk_span.instrumentation_info
254+
)
255+
)
256+
# If we haven't seen the InstrumentationInfo for this Resource yet, add it to the map
257+
if not instrumentation_library_spans:
245258
if sdk_span.instrumentation_info is not None:
246-
instrumentation_library_spans = (
247-
InstrumentationLibrarySpans(
248-
instrumentation_library=InstrumentationLibrary(
249-
name=sdk_span.instrumentation_info.name,
250-
version=sdk_span.instrumentation_info.version,
251-
)
259+
instrumentation_library_spans_map[
260+
sdk_span.instrumentation_info
261+
] = InstrumentationLibrarySpans(
262+
instrumentation_library=InstrumentationLibrary(
263+
name=sdk_span.instrumentation_info.name,
264+
version=sdk_span.instrumentation_info.version,
252265
)
253266
)
254-
255267
else:
256-
instrumentation_library_spans = (
257-
InstrumentationLibrarySpans()
258-
)
259-
260-
sdk_resource_instrumentation_library_spans[
261-
sdk_span.resource
262-
] = instrumentation_library_spans
263-
268+
# If no InstrumentationInfo, store in None key
269+
instrumentation_library_spans_map[
270+
sdk_span.instrumentation_info
271+
] = InstrumentationLibrarySpans()
272+
instrumentation_library_spans = (
273+
instrumentation_library_spans_map.get(
274+
sdk_span.instrumentation_info
275+
)
276+
)
264277
self._collector_span_kwargs = {}
265278

266279
self._translate_name(sdk_span)
@@ -292,9 +305,9 @@ def _translate_data(
292305
"SPAN_KIND_{}".format(sdk_span.kind.name),
293306
)
294307

295-
sdk_resource_instrumentation_library_spans[
296-
sdk_span.resource
297-
].spans.append(CollectorSpan(**self._collector_span_kwargs))
308+
instrumentation_library_spans.spans.append(
309+
CollectorSpan(**self._collector_span_kwargs)
310+
)
298311

299312
return ExportTraceServiceRequest(
300313
resource_spans=get_resource_data(

exporter/opentelemetry-exporter-otlp-proto-grpc/tests/test_otlp_trace_exporter.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,44 @@ def setUp(self):
176176
),
177177
)
178178

179+
self.span2 = _Span(
180+
"b",
181+
context=Mock(
182+
**{
183+
"trace_state": OrderedDict([("a", "b"), ("c", "d")]),
184+
"span_id": 10217189687419569865,
185+
"trace_id": 67545097771067222548457157018666467027,
186+
}
187+
),
188+
resource=SDKResource(OrderedDict([("a", 2), ("b", False)])),
189+
parent=Mock(**{"span_id": 12345}),
190+
instrumentation_info=InstrumentationInfo(
191+
name="name", version="version"
192+
),
193+
)
194+
195+
self.span3 = _Span(
196+
"c",
197+
context=Mock(
198+
**{
199+
"trace_state": OrderedDict([("a", "b"), ("c", "d")]),
200+
"span_id": 10217189687419569865,
201+
"trace_id": 67545097771067222548457157018666467027,
202+
}
203+
),
204+
resource=SDKResource(OrderedDict([("a", 1), ("b", False)])),
205+
parent=Mock(**{"span_id": 12345}),
206+
instrumentation_info=InstrumentationInfo(
207+
name="name2", version="version2"
208+
),
209+
)
210+
179211
self.span.start()
180212
self.span.end()
213+
self.span2.start()
214+
self.span2.end()
215+
self.span3.start()
216+
self.span3.end()
181217

182218
def tearDown(self):
183219
self.server.stop(None)
@@ -505,6 +541,181 @@ def test_translate_spans(self):
505541
# pylint: disable=protected-access
506542
self.assertEqual(expected, self.exporter._translate_data([self.span]))
507543

544+
def test_translate_spans_multi(self):
545+
expected = ExportTraceServiceRequest(
546+
resource_spans=[
547+
ResourceSpans(
548+
resource=OTLPResource(
549+
attributes=[
550+
KeyValue(key="a", value=AnyValue(int_value=1)),
551+
KeyValue(
552+
key="b", value=AnyValue(bool_value=False)
553+
),
554+
]
555+
),
556+
instrumentation_library_spans=[
557+
InstrumentationLibrarySpans(
558+
instrumentation_library=InstrumentationLibrary(
559+
name="name", version="version"
560+
),
561+
spans=[
562+
OTLPSpan(
563+
# pylint: disable=no-member
564+
name="a",
565+
start_time_unix_nano=self.span.start_time,
566+
end_time_unix_nano=self.span.end_time,
567+
trace_state="a=b,c=d",
568+
span_id=int.to_bytes(
569+
10217189687419569865, 8, "big"
570+
),
571+
trace_id=int.to_bytes(
572+
67545097771067222548457157018666467027,
573+
16,
574+
"big",
575+
),
576+
parent_span_id=(
577+
b"\000\000\000\000\000\00009"
578+
),
579+
kind=(
580+
OTLPSpan.SpanKind.SPAN_KIND_INTERNAL
581+
),
582+
attributes=[
583+
KeyValue(
584+
key="a",
585+
value=AnyValue(int_value=1),
586+
),
587+
KeyValue(
588+
key="b",
589+
value=AnyValue(bool_value=True),
590+
),
591+
],
592+
events=[
593+
OTLPSpan.Event(
594+
name="a",
595+
time_unix_nano=1591240820506462784,
596+
attributes=[
597+
KeyValue(
598+
key="a",
599+
value=AnyValue(
600+
int_value=1
601+
),
602+
),
603+
KeyValue(
604+
key="b",
605+
value=AnyValue(
606+
bool_value=False
607+
),
608+
),
609+
],
610+
)
611+
],
612+
status=Status(code=0, message=""),
613+
links=[
614+
OTLPSpan.Link(
615+
trace_id=int.to_bytes(
616+
1, 16, "big"
617+
),
618+
span_id=int.to_bytes(2, 8, "big"),
619+
attributes=[
620+
KeyValue(
621+
key="a",
622+
value=AnyValue(
623+
int_value=1
624+
),
625+
),
626+
KeyValue(
627+
key="b",
628+
value=AnyValue(
629+
bool_value=False
630+
),
631+
),
632+
],
633+
)
634+
],
635+
)
636+
],
637+
),
638+
InstrumentationLibrarySpans(
639+
instrumentation_library=InstrumentationLibrary(
640+
name="name2", version="version2"
641+
),
642+
spans=[
643+
OTLPSpan(
644+
# pylint: disable=no-member
645+
name="c",
646+
start_time_unix_nano=self.span3.start_time,
647+
end_time_unix_nano=self.span3.end_time,
648+
trace_state="a=b,c=d",
649+
span_id=int.to_bytes(
650+
10217189687419569865, 8, "big"
651+
),
652+
trace_id=int.to_bytes(
653+
67545097771067222548457157018666467027,
654+
16,
655+
"big",
656+
),
657+
parent_span_id=(
658+
b"\000\000\000\000\000\00009"
659+
),
660+
kind=(
661+
OTLPSpan.SpanKind.SPAN_KIND_INTERNAL
662+
),
663+
status=Status(code=0, message=""),
664+
)
665+
],
666+
),
667+
],
668+
),
669+
ResourceSpans(
670+
resource=OTLPResource(
671+
attributes=[
672+
KeyValue(key="a", value=AnyValue(int_value=2)),
673+
KeyValue(
674+
key="b", value=AnyValue(bool_value=False)
675+
),
676+
]
677+
),
678+
instrumentation_library_spans=[
679+
InstrumentationLibrarySpans(
680+
instrumentation_library=InstrumentationLibrary(
681+
name="name", version="version"
682+
),
683+
spans=[
684+
OTLPSpan(
685+
# pylint: disable=no-member
686+
name="b",
687+
start_time_unix_nano=self.span2.start_time,
688+
end_time_unix_nano=self.span2.end_time,
689+
trace_state="a=b,c=d",
690+
span_id=int.to_bytes(
691+
10217189687419569865, 8, "big"
692+
),
693+
trace_id=int.to_bytes(
694+
67545097771067222548457157018666467027,
695+
16,
696+
"big",
697+
),
698+
parent_span_id=(
699+
b"\000\000\000\000\000\00009"
700+
),
701+
kind=(
702+
OTLPSpan.SpanKind.SPAN_KIND_INTERNAL
703+
),
704+
status=Status(code=0, message=""),
705+
)
706+
],
707+
)
708+
],
709+
),
710+
]
711+
)
712+
713+
# pylint: disable=protected-access
714+
self.assertEqual(
715+
expected,
716+
self.exporter._translate_data([self.span, self.span2, self.span3]),
717+
)
718+
508719
def _check_translated_status(
509720
self,
510721
translated: ExportTraceServiceRequest,

0 commit comments

Comments
 (0)