From 1112792b18bb177ba58fa11ebaecc9597041a73c Mon Sep 17 00:00:00 2001 From: Emmanuel Courreges Date: Thu, 30 Jul 2020 17:18:38 +0200 Subject: [PATCH] Add proper length zero padding to hex strings of traceId, spanId, parentId sent on the wire (#908) --- .../CHANGELOG.md | 4 +- .../opentelemetry/exporter/zipkin/__init__.py | 9 +-- .../tests/test_zipkin_exporter.py | 66 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/exporter/opentelemetry-exporter-zipkin/CHANGELOG.md b/exporter/opentelemetry-exporter-zipkin/CHANGELOG.md index 2218d44c20..1063d35cf5 100644 --- a/exporter/opentelemetry-exporter-zipkin/CHANGELOG.md +++ b/exporter/opentelemetry-exporter-zipkin/CHANGELOG.md @@ -4,6 +4,8 @@ - Change package name to opentelemetry-exporter-zipkin ([#953](https://github.com/open-telemetry/opentelemetry-python/pull/953)) +- Add proper length zero padding to hex strings of traceId, spanId, parentId sent on the wire, for compatibility with jaeger-collector + ([#908](https://github.com/open-telemetry/opentelemetry-python/pull/908)) ## 0.8b0 @@ -23,4 +25,4 @@ Released 2020-05-12 Released 2020-02-21 -- Initial release \ No newline at end of file +- Initial release diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py index 10bd6e93e9..b0eb1bce0f 100644 --- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py +++ b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py @@ -168,8 +168,9 @@ def _translate_to_zipkin(self, spans: Sequence[Span]): duration_mus = _nsec_to_usec_round(span.end_time - span.start_time) zipkin_span = { - "traceId": format(trace_id, "x"), - "id": format(span_id, "x"), + # Ensure left-zero-padding of traceId, spanId, parentId + "traceId": format(trace_id, "032x"), + "id": format(span_id, "016x"), "name": span.name, "timestamp": start_timestamp_mus, "duration": duration_mus, @@ -184,10 +185,10 @@ def _translate_to_zipkin(self, spans: Sequence[Span]): if isinstance(span.parent, Span): zipkin_span["parentId"] = format( - span.parent.get_context().span_id, "x" + span.parent.get_context().span_id, "016x" ) elif isinstance(span.parent, SpanContext): - zipkin_span["parentId"] = format(span.parent.span_id, "x") + zipkin_span["parentId"] = format(span.parent.span_id, "016x") zipkin_spans.append(zipkin_span) return zipkin_spans diff --git a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py index 9f6da2e240..f6e24a1495 100644 --- a/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py +++ b/exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py @@ -264,6 +264,72 @@ def test_export(self): headers={"Content-Type": "application/json"}, ) + # pylint: disable=too-many-locals + def test_zero_padding(self): + """test that hex ids starting with 0 + are properly padded to 16 or 32 hex chars + when exported + """ + + span_names = "testZeroes" + trace_id = 0x0E0C63257DE34C926F9EFCD03927272E + span_id = 0x04BF92DEEFC58C92 + parent_id = 0x0AAAAAAAAAAAAAAA + + start_time = 683647322 * 10 ** 9 # in ns + duration = 50 * 10 ** 6 + end_time = start_time + duration + + span_context = trace_api.SpanContext( + trace_id, + span_id, + is_remote=False, + trace_flags=TraceFlags(TraceFlags.SAMPLED), + ) + parent_context = trace_api.SpanContext( + trace_id, parent_id, is_remote=False + ) + + otel_span = trace.Span( + name=span_names[0], context=span_context, parent=parent_context, + ) + + otel_span.start(start_time=start_time) + otel_span.end(end_time=end_time) + + service_name = "test-service" + local_endpoint = {"serviceName": service_name, "port": 9411} + + exporter = ZipkinSpanExporter(service_name) + # Check traceId are properly lowercase 16 or 32 hex + expected = [ + { + "traceId": "0e0c63257de34c926f9efcd03927272e", + "id": "04bf92deefc58c92", + "name": span_names[0], + "timestamp": start_time // 10 ** 3, + "duration": duration // 10 ** 3, + "localEndpoint": local_endpoint, + "kind": None, + "tags": {}, + "annotations": None, + "debug": True, + "parentId": "0aaaaaaaaaaaaaaa", + } + ] + + mock_post = MagicMock() + with patch("requests.post", mock_post): + mock_post.return_value = MockResponse(200) + status = exporter.export([otel_span]) + self.assertEqual(SpanExportResult.SUCCESS, status) + + mock_post.assert_called_with( + url="http://localhost:9411/api/v2/spans", + data=json.dumps(expected), + headers={"Content-Type": "application/json"}, + ) + @patch("requests.post") def test_invalid_response(self, mock_post): mock_post.return_value = MockResponse(404)