Skip to content

Commit 7654c90

Browse files
mauriciovasquezbernalAlex Boten
authored and
Alex Boten
committed
sdk: Improve console span exporter (open-telemetry#505)
The current version of the exporter prints everything in a single line, making it difficult to read. It's also missing events, links and attributes. This commit changes the console span exporter to use multiple lines and also adds the missing information about attributes, events and links.
1 parent 58f9166 commit 7654c90

File tree

5 files changed

+83
-20
lines changed

5 files changed

+83
-20
lines changed

Diff for: docs/examples/basic_tracer/tests/test_tracer.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ def test_basic_tracer(self):
2525
(sys.executable, test_script)
2626
).decode()
2727

28-
self.assertIn('name="foo"', output)
29-
self.assertIn('name="bar"', output)
30-
self.assertIn('name="baz"', output)
28+
self.assertIn('"name": "foo"', output)
29+
self.assertIn('"name": "bar"', output)
30+
self.assertIn('"name": "baz"', output)

Diff for: docs/examples/http/tests/test_http.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_http(self):
3232
output = subprocess.check_output(
3333
(sys.executable, test_script)
3434
).decode()
35-
self.assertIn('name="/"', output)
35+
self.assertIn('"name": "/"', output)
3636

3737
@classmethod
3838
def teardown_class(cls):

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

+76-13
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515

1616
import abc
1717
import atexit
18+
import json
1819
import logging
20+
import os
1921
import random
2022
import threading
23+
from collections import OrderedDict
2124
from contextlib import contextmanager
2225
from types import TracebackType
2326
from typing import Iterator, MutableSequence, Optional, Sequence, Tuple, Type
@@ -276,19 +279,79 @@ def __repr__(self):
276279
type(self).__name__, self.name, self.context
277280
)
278281

279-
def __str__(self):
280-
return (
281-
'{}(name="{}", context={}, kind={}, '
282-
"parent={}, start_time={}, end_time={})"
283-
).format(
284-
type(self).__name__,
285-
self.name,
286-
self.context,
287-
self.kind,
288-
repr(self.parent),
289-
util.ns_to_iso_str(self.start_time) if self.start_time else "None",
290-
util.ns_to_iso_str(self.end_time) if self.end_time else "None",
291-
)
282+
@staticmethod
283+
def _format_context(context):
284+
x_ctx = OrderedDict()
285+
x_ctx["trace_id"] = trace_api.format_trace_id(context.trace_id)
286+
x_ctx["span_id"] = trace_api.format_span_id(context.span_id)
287+
x_ctx["trace_state"] = repr(context.trace_state)
288+
return x_ctx
289+
290+
@staticmethod
291+
def _format_attributes(attributes):
292+
if isinstance(attributes, BoundedDict):
293+
return attributes._dict # pylint: disable=protected-access
294+
return attributes
295+
296+
@staticmethod
297+
def _format_events(events):
298+
f_events = []
299+
for event in events:
300+
f_event = OrderedDict()
301+
f_event["name"] = event.name
302+
f_event["timestamp"] = util.ns_to_iso_str(event.timestamp)
303+
f_event["attributes"] = Span._format_attributes(event.attributes)
304+
f_events.append(f_event)
305+
return f_events
306+
307+
@staticmethod
308+
def _format_links(links):
309+
f_links = []
310+
for link in links:
311+
f_link = OrderedDict()
312+
f_link["context"] = Span._format_context(link.context)
313+
f_link["attributes"] = Span._format_attributes(link.attributes)
314+
f_links.append(f_link)
315+
return f_links
316+
317+
def to_json(self):
318+
parent_id = None
319+
if self.parent is not None:
320+
if isinstance(self.parent, Span):
321+
ctx = self.parent.context
322+
parent_id = trace_api.format_span_id(ctx.span_id)
323+
elif isinstance(self.parent, SpanContext):
324+
parent_id = trace_api.format_span_id(self.parent.span_id)
325+
326+
start_time = None
327+
if self.start_time:
328+
start_time = util.ns_to_iso_str(self.start_time)
329+
330+
end_time = None
331+
if self.end_time:
332+
end_time = util.ns_to_iso_str(self.end_time)
333+
334+
if self.status is not None:
335+
status = OrderedDict()
336+
status["canonical_code"] = str(self.status.canonical_code.name)
337+
if self.status.description:
338+
status["description"] = self.status.description
339+
340+
f_span = OrderedDict()
341+
342+
f_span["name"] = self.name
343+
f_span["context"] = self._format_context(self.context)
344+
f_span["kind"] = str(self.kind)
345+
f_span["parent_id"] = parent_id
346+
f_span["start_time"] = start_time
347+
f_span["end_time"] = end_time
348+
if self.status is not None:
349+
f_span["status"] = status
350+
f_span["attributes"] = self._format_attributes(self.attributes)
351+
f_span["events"] = self._format_events(self.events)
352+
f_span["links"] = self._format_links(self.links)
353+
354+
return json.dumps(f_span, indent=4)
292355

293356
def get_context(self):
294357
return self.context

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/trace/export/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class ConsoleSpanExporter(SpanExporter):
270270
def __init__(
271271
self,
272272
out: typing.IO = sys.stdout,
273-
formatter: typing.Callable[[Span], str] = lambda span: str(span)
273+
formatter: typing.Callable[[Span], str] = lambda span: span.to_json()
274274
+ os.linesep,
275275
):
276276
self.out = out

Diff for: opentelemetry-sdk/tests/trace/export/test_export.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ def test_export(self): # pylint: disable=no-self-use
286286

287287
# Mocking stdout interferes with debugging and test reporting, mock on
288288
# the exporter instance instead.
289-
span = trace.Span("span name", mock.Mock())
289+
span = trace.Span("span name", trace_api.INVALID_SPAN_CONTEXT)
290290
with mock.patch.object(exporter, "out") as mock_stdout:
291291
exporter.export([span])
292-
mock_stdout.write.assert_called_once_with(str(span) + os.linesep)
292+
mock_stdout.write.assert_called_once_with(span.to_json() + os.linesep)
293293
self.assertEqual(mock_stdout.write.call_count, 1)
294294
self.assertEqual(mock_stdout.flush.call_count, 1)
295295

0 commit comments

Comments
 (0)