Skip to content

Commit 663ac6e

Browse files
committed
Make zipkin tag value length configurable
Zipkin exporter truncates tag values to a maximum length of 128 characters. This commit makes this value configurable while keeping 128 as the default value.
1 parent b8a8016 commit 663ac6e

File tree

3 files changed

+95
-36
lines changed

3 files changed

+95
-36
lines changed

exporter/opentelemetry-exporter-zipkin/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Zipkin exporter now accepts a ``max_tag_value_length`` attribute to customize the maximum allowed size a tag value can have.
6+
57
## Version 0.13b0
68

79
Released 2020-09-17

exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py

+46-36
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474

7575
DEFAULT_RETRY = False
7676
DEFAULT_URL = "http://localhost:9411/api/v2/spans"
77+
DEFAULT_MAX_TAG_VALUE_LENGTH = 128
7778
ZIPKIN_HEADERS = {"Content-Type": "application/json"}
7879

7980
SPAN_KIND_MAP = {
@@ -108,6 +109,7 @@ def __init__(
108109
ipv4: Optional[str] = None,
109110
ipv6: Optional[str] = None,
110111
retry: Optional[str] = DEFAULT_RETRY,
112+
max_tag_value_length=DEFAULT_MAX_TAG_VALUE_LENGTH,
111113
):
112114
self.service_name = service_name
113115
if url is None:
@@ -122,6 +124,7 @@ def __init__(
122124
self.ipv4 = ipv4
123125
self.ipv6 = ipv6
124126
self.retry = retry
127+
self.max_tag_value_length = max_tag_value_length
125128

126129
def export(self, spans: Sequence[Span]) -> SpanExportResult:
127130
zipkin_spans = self._translate_to_zipkin(spans)
@@ -141,6 +144,9 @@ def export(self, spans: Sequence[Span]) -> SpanExportResult:
141144
return SpanExportResult.FAILURE
142145
return SpanExportResult.SUCCESS
143146

147+
def shutdown(self) -> None:
148+
pass
149+
144150
def _translate_to_zipkin(self, spans: Sequence[Span]):
145151

146152
local_endpoint = {"serviceName": self.service_name, "port": self.port}
@@ -171,8 +177,10 @@ def _translate_to_zipkin(self, spans: Sequence[Span]):
171177
"duration": duration_mus,
172178
"localEndpoint": local_endpoint,
173179
"kind": SPAN_KIND_MAP[span.kind],
174-
"tags": _extract_tags_from_span(span),
175-
"annotations": _extract_annotations_from_events(span.events),
180+
"tags": self._extract_tags_from_span(span),
181+
"annotations": self._extract_annotations_from_events(
182+
span.events
183+
),
176184
}
177185

178186
if span.instrumentation_info is not None:
@@ -205,42 +213,44 @@ def _translate_to_zipkin(self, spans: Sequence[Span]):
205213
zipkin_spans.append(zipkin_span)
206214
return zipkin_spans
207215

208-
def shutdown(self) -> None:
209-
pass
210-
216+
def _extract_tags_from_dict(self, tags_dict):
217+
tags = {}
218+
if not tags_dict:
219+
return tags
220+
for attribute_key, attribute_value in tags_dict.items():
221+
if isinstance(attribute_value, (int, bool, float)):
222+
value = str(attribute_value)
223+
elif isinstance(attribute_value, str):
224+
value = attribute_value
225+
else:
226+
logger.warning("Could not serialize tag %s", attribute_key)
227+
continue
228+
229+
if self.max_tag_value_length > 0:
230+
value = value[: self.max_tag_value_length]
231+
tags[attribute_key] = value
232+
return tags
211233

212-
def _extract_tags_from_dict(tags_dict):
213-
tags = {}
214-
if not tags_dict:
234+
def _extract_tags_from_span(self, span: Span):
235+
tags = self._extract_tags_from_dict(getattr(span, "attributes", None))
236+
if span.resource:
237+
tags.update(self._extract_tags_from_dict(span.resource.attributes))
215238
return tags
216-
for attribute_key, attribute_value in tags_dict.items():
217-
if isinstance(attribute_value, (int, bool, float)):
218-
value = str(attribute_value)
219-
elif isinstance(attribute_value, str):
220-
value = attribute_value[:128]
221-
else:
222-
logger.warning("Could not serialize tag %s", attribute_key)
223-
continue
224-
tags[attribute_key] = value
225-
return tags
226-
227-
228-
def _extract_tags_from_span(span: Span):
229-
tags = _extract_tags_from_dict(getattr(span, "attributes", None))
230-
if span.resource:
231-
tags.update(_extract_tags_from_dict(span.resource.attributes))
232-
return tags
233-
234-
235-
def _extract_annotations_from_events(events):
236-
return (
237-
[
238-
{"timestamp": _nsec_to_usec_round(e.timestamp), "value": e.name}
239-
for e in events
240-
]
241-
if events
242-
else None
243-
)
239+
240+
def _extract_annotations_from_events(
241+
self, events
242+
): # pylint: disable=R0201
243+
return (
244+
[
245+
{
246+
"timestamp": _nsec_to_usec_round(e.timestamp),
247+
"value": e.name,
248+
}
249+
for e in events
250+
]
251+
if events
252+
else None
253+
)
244254

245255

246256
def _nsec_to_usec_round(nsec):

exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py

+47
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,50 @@ def test_invalid_response(self, mock_post):
361361
exporter = ZipkinSpanExporter("test-service")
362362
status = exporter.export(spans)
363363
self.assertEqual(SpanExportResult.FAILURE, status)
364+
365+
def test_max_tag_length(self):
366+
service_name = "test-service"
367+
368+
span_context = trace_api.SpanContext(
369+
0x0E0C63257DE34C926F9EFCD03927272E,
370+
0x04BF92DEEFC58C92,
371+
is_remote=False,
372+
trace_flags=TraceFlags(TraceFlags.SAMPLED),
373+
)
374+
375+
span = trace.Span(name="test-span", context=span_context,)
376+
377+
span.start()
378+
span.resource = Resource({})
379+
# added here to preserve order
380+
span.set_attribute("k1", "v" * 500)
381+
span.set_attribute("k2", "v" * 50)
382+
span.set_status(
383+
Status(StatusCanonicalCode.UNKNOWN, "Example description")
384+
)
385+
span.end()
386+
387+
exporter = ZipkinSpanExporter(service_name)
388+
mock_post = MagicMock()
389+
with patch("requests.post", mock_post):
390+
mock_post.return_value = MockResponse(200)
391+
status = exporter.export([span])
392+
self.assertEqual(SpanExportResult.SUCCESS, status)
393+
394+
_, kwargs = mock_post.call_args
395+
396+
tags = json.loads(kwargs["data"])[0]["tags"]
397+
self.assertEqual(len(tags["k1"]), 128)
398+
self.assertEqual(len(tags["k2"]), 50)
399+
400+
exporter = ZipkinSpanExporter(service_name, max_tag_value_length=2)
401+
mock_post = MagicMock()
402+
with patch("requests.post", mock_post):
403+
mock_post.return_value = MockResponse(200)
404+
status = exporter.export([span])
405+
self.assertEqual(SpanExportResult.SUCCESS, status)
406+
407+
_, kwargs = mock_post.call_args
408+
tags = json.loads(kwargs["data"])[0]["tags"]
409+
self.assertEqual(len(tags["k1"]), 2)
410+
self.assertEqual(len(tags["k2"]), 2)

0 commit comments

Comments
 (0)