Repository: census-instrumentation/opencensus-python
Pull request #110 (2018-01-31 20:15:54)

Fix zipkin_exporter to export span names and tags correctly
Tested with zipkin jar running locally.


Patch:
diff --git a/opencensus/trace/exporters/zipkin_exporter.py b/opencensus/trace/exporters/zipkin_exporter.py
index 5f9553d..8ac9ac6 100644
--- a/opencensus/trace/exporters/zipkin_exporter.py
+++ b/opencensus/trace/exporters/zipkin_exporter.py
@@ -87,7 +87,7 @@ class ZipkinExporter(base.Exporter):
             self.endpoint)
 
     def emit(self, trace):
-        """Send trace to Zipkin server, default using the v1 API.
+        """Send trace to Zipkin server, default using the v2 API.
 
         :type trace: dict
         :param trace: Trace data in dictionary format.
@@ -151,11 +151,11 @@ class ZipkinExporter(base.Exporter):
             zipkin_span = {
                 'traceId': trace_id,
                 'id': str(span.get('spanId')),
-                'name': span.get('name'),
+                'name': span.get('displayName', {}).get('value'),
                 'timestamp': int(round(start_timestamp_ms)),
                 'duration': int(round(duration_ms)),
                 'localEndpoint': local_endpoint,
-                'tags': span.get('attributes'),
+                'tags': _extract_tags_from_span(span),
             }
 
             span_kind = span.get('kind')
@@ -174,3 +174,22 @@ class ZipkinExporter(base.Exporter):
             zipkin_spans.append(zipkin_span)
 
         return zipkin_spans
+
+
+def _extract_tags_from_span(span):
+    tags = {}
+    for attribute_key, attribute_value in span.get(
+            'attributes', {}).get('attributeMap', {}).items():
+        if not isinstance(attribute_value, dict):
+            continue
+        if attribute_value.get('string_value') is not None:
+            value = attribute_value.get('string_value').get('value')
+        elif attribute_value.get('int_value') is not None:
+            value = str(attribute_value.get('int_value'))
+        elif attribute_value.get('bool_value') is not None:
+            value = str(attribute_value.get('bool_value'))
+        else:
+            logging.warn('Could not serialize tag {}'.format(attribute_key))
+            continue
+        tags[attribute_key] = value
+    return tags


Test patch:
diff --git a/tests/unit/trace/exporters/test_zipkin_exporter.py b/tests/unit/trace/exporters/test_zipkin_exporter.py
index 0f82920..ace3bc8 100644
--- a/tests/unit/trace/exporters/test_zipkin_exporter.py
+++ b/tests/unit/trace/exporters/test_zipkin_exporter.py
@@ -91,39 +91,55 @@ class TestZipkinExporter(unittest.TestCase):
 
     def test_translate_to_zipkin_span_kind_none(self):
         span1 = {
-            'name': 'child_span',
+            'displayName': {'value': 'child_span'},
             'parentSpanId': 1111111111,
             'spanId': 1234567890,
             'startTime': '2017-08-15T18:02:26.071158Z',
             'endTime': '2017-08-15T18:02:36.071158Z',
             'attributes': {
-                'key': 'test_key',
-                'value': 'test_value',
+                'attributeMap': {
+                    'test_key': {
+                        'string_value': {
+                            'value': 'test_value'
+                        }
+                    }
+                }
             },
         }
 
         span2 = {
-            'name': 'child_span',
+            'displayName': {'value': 'child_span'},
             'kind': 0,
             'parentSpanId': 1111111111,
             'spanId': 1234567890,
             'startTime': '2017-08-15T18:02:26.071158Z',
             'endTime': '2017-08-15T18:02:36.071158Z',
             'attributes': {
-                'key': 'test_key',
-                'value': 'test_value',
+                'attributeMap': {
+                    'test_key':  {
+                        'int_value': 1
+                    }
+                }
             },
         }
 
         span3 = {
-            'name': 'child_span',
+            'displayName': {'value': 'child_span'},
             'kind': 1,
             'spanId': 1234567890,
             'startTime': '2017-08-15T18:02:26.071158Z',
             'endTime': '2017-08-15T18:02:36.071158Z',
             'attributes': {
-                'key': 'test_key',
-                'value': 'test_value',
+                'attributeMap': {
+                    'test_key':  {
+                        'bool_value': False
+                    },
+                    # these tags are malformed and should be omitted
+                    'test_key2': 'raw_value',
+                    'test_key3': {
+                        'float_value': 0.1
+                    },
+                }
             },
         }
 
@@ -145,10 +161,7 @@ class TestZipkinExporter(unittest.TestCase):
                 'timestamp': 1502820146000000,
                 'duration': 10000000,
                 'localEndpoint': local_endpoint,
-                'tags': {
-                    'key': 'test_key',
-                    'value': 'test_value',
-                },
+                'tags': {'test_key': 'test_value'},
             },
             {
                 'traceId': '6e0c63257de34c92bf9efcd03927272e',
@@ -158,10 +171,7 @@ class TestZipkinExporter(unittest.TestCase):
                 'timestamp': 1502820146000000,
                 'duration': 10000000,
                 'localEndpoint': local_endpoint,
-                'tags': {
-                    'key': 'test_key',
-                    'value': 'test_value',
-                },
+                'tags': {'test_key': '1'},
             },
             {
                 'traceId': '6e0c63257de34c92bf9efcd03927272e',
@@ -170,10 +180,7 @@ class TestZipkinExporter(unittest.TestCase):
                 'timestamp': 1502820146000000,
                 'duration': 10000000,
                 'localEndpoint': local_endpoint,
-                'tags': {
-                    'key': 'test_key',
-                    'value': 'test_value',
-                },
+                'tags': {'test_key': 'False'},
                 'kind': 'SERVER',
             }
         ]