We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Zipkin exporter must map status
As per spec: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk_exporters/zipkin.md#status

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 This library allows to export tracing data to `Zipkin <https://zipkin.io/>`_.
17 
18 Usage
19 -----
20 
21 The **OpenTelemetry Zipkin Exporter** allows to export `OpenTelemetry`_ traces to `Zipkin`_.
22 This exporter always send traces to the configured Zipkin collector using HTTP.
23 
24 
25 .. _Zipkin: https://zipkin.io/
26 .. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/
27 .. _Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md#zipkin-exporter
28 
29 .. code:: python
30 
31     from opentelemetry import trace
32     from opentelemetry.exporter import zipkin
33     from opentelemetry.sdk.trace import TracerProvider
34     from opentelemetry.sdk.trace.export import BatchExportSpanProcessor
35 
36     trace.set_tracer_provider(TracerProvider())
37     tracer = trace.get_tracer(__name__)
38 
39     # create a ZipkinSpanExporter
40     zipkin_exporter = zipkin.ZipkinSpanExporter(
41         service_name="my-helloworld-service",
42         # optional:
43         # url="http://localhost:9411/api/v2/spans",
44         # ipv4="",
45         # ipv6="",
46         # retry=False,
47     )
48 
49     # Create a BatchExportSpanProcessor and add the exporter to it
50     span_processor = BatchExportSpanProcessor(zipkin_exporter)
51 
52     # add to the tracer
53     trace.get_tracer_provider().add_span_processor(span_processor)
54 
55     with tracer.start_as_current_span("foo"):
56         print("Hello world!")
57 
58 The exporter supports endpoint configuration via the OTEL_EXPORTER_ZIPKIN_ENDPOINT environment variables as defined in the `Specification`_
59 
60 API
61 ---
62 """
63 
64 import json
65 import logging
66 import os
67 from typing import Optional, Sequence
68 from urllib.parse import urlparse
69 
70 import requests
71 
72 from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
73 from opentelemetry.trace import Span, SpanContext, SpanKind
74 
75 DEFAULT_RETRY = False
76 DEFAULT_URL = "http://localhost:9411/api/v2/spans"
77 ZIPKIN_HEADERS = {"Content-Type": "application/json"}
78 
79 SPAN_KIND_MAP = {
80     SpanKind.INTERNAL: None,
81     SpanKind.SERVER: "SERVER",
82     SpanKind.CLIENT: "CLIENT",
83     SpanKind.PRODUCER: "PRODUCER",
84     SpanKind.CONSUMER: "CONSUMER",
85 }
86 
87 SUCCESS_STATUS_CODES = (200, 202)
88 
89 logger = logging.getLogger(__name__)
90 
91 
92 class ZipkinSpanExporter(SpanExporter):
93     """Zipkin span exporter for OpenTelemetry.
94 
95     Args:
96         service_name: Service that logged an annotation in a trace.Classifier
97             when query for spans.
98         url: The Zipkin endpoint URL
99         ipv4: Primary IPv4 address associated with this connection.
100         ipv6: Primary IPv6 address associated with this connection.
101         retry: Set to True to configure the exporter to retry on failure.
102     """
103 
104     def __init__(
105         self,
106         service_name: str,
107         url: str = None,
108         ipv4: Optional[str] = None,
109         ipv6: Optional[str] = None,
110         retry: Optional[str] = DEFAULT_RETRY,
111     ):
112         self.service_name = service_name
113         if url is None:
114             self.url = os.environ.get(
115                 "OTEL_EXPORTER_ZIPKIN_ENDPOINT", DEFAULT_URL
116             )
117         else:
118             self.url = url
119 
120         self.port = urlparse(self.url).port
121 
122         self.ipv4 = ipv4
123         self.ipv6 = ipv6
124         self.retry = retry
125 
126     def export(self, spans: Sequence[Span]) -> SpanExportResult:
127         zipkin_spans = self._translate_to_zipkin(spans)
128         result = requests.post(
129             url=self.url, data=json.dumps(zipkin_spans), headers=ZIPKIN_HEADERS
130         )
131 
132         if result.status_code not in SUCCESS_STATUS_CODES:
133             logger.error(
134                 "Traces cannot be uploaded; status code: %s, message %s",
135                 result.status_code,
136                 result.text,
137             )
138 
139             if self.retry:
140                 return SpanExportResult.FAILURE
141             return SpanExportResult.FAILURE
142         return SpanExportResult.SUCCESS
143 
144     def _translate_to_zipkin(self, spans: Sequence[Span]):
145 
146         local_endpoint = {"serviceName": self.service_name, "port": self.port}
147 
148         if self.ipv4 is not None:
149             local_endpoint["ipv4"] = self.ipv4
150 
151         if self.ipv6 is not None:
152             local_endpoint["ipv6"] = self.ipv6
153 
154         zipkin_spans = []
155         for span in spans:
156             context = span.get_context()
157             trace_id = context.trace_id
158             span_id = context.span_id
159 
160             # Timestamp in zipkin spans is int of microseconds.
161             # see: https://zipkin.io/pages/instrumenting.html
162             start_timestamp_mus = _nsec_to_usec_round(span.start_time)
163             duration_mus = _nsec_to_usec_round(span.end_time - span.start_time)
164 
165             zipkin_span = {
166                 # Ensure left-zero-padding of traceId, spanId, parentId
167                 "traceId": format(trace_id, "032x"),
168                 "id": format(span_id, "016x"),
169                 "name": span.name,
170                 "timestamp": start_timestamp_mus,
171                 "duration": duration_mus,
172                 "localEndpoint": local_endpoint,
173                 "kind": SPAN_KIND_MAP[span.kind],
174                 "tags": _extract_tags_from_span(span),
175                 "annotations": _extract_annotations_from_events(span.events),
176             }
177 
178             if span.instrumentation_info is not None:
179                 zipkin_span["tags"][
180                     "otel.instrumentation_library.name"
181                 ] = span.instrumentation_info.name
182                 zipkin_span["tags"][
183                     "otel.instrumentation_library.version"
184                 ] = span.instrumentation_info.version
185 
186             if context.trace_flags.sampled:
187                 zipkin_span["debug"] = True
188 
189             if isinstance(span.parent, Span):
190                 zipkin_span["parentId"] = format(
191                     span.parent.get_context().span_id, "016x"
192                 )
193             elif isinstance(span.parent, SpanContext):
194                 zipkin_span["parentId"] = format(span.parent.span_id, "016x")
195 
196             zipkin_spans.append(zipkin_span)
197         return zipkin_spans
198 
199     def shutdown(self) -> None:
200         pass
201 
202 
203 def _extract_tags_from_dict(tags_dict):
204     tags = {}
205     if not tags_dict:
206         return tags
207     for attribute_key, attribute_value in tags_dict.items():
208         if isinstance(attribute_value, (int, bool, float)):
209             value = str(attribute_value)
210         elif isinstance(attribute_value, str):
211             value = attribute_value[:128]
212         else:
213             logger.warning("Could not serialize tag %s", attribute_key)
214             continue
215         tags[attribute_key] = value
216     return tags
217 
218 
219 def _extract_tags_from_span(span: Span):
220     tags = _extract_tags_from_dict(getattr(span, "attributes", None))
221     if span.resource:
222         tags.update(_extract_tags_from_dict(span.resource.attributes))
223     return tags
224 
225 
226 def _extract_annotations_from_events(events):
227     return (
228         [
229             {"timestamp": _nsec_to_usec_round(e.timestamp), "value": e.name}
230             for e in events
231         ]
232         if events
233         else None
234     )
235 
236 
237 def _nsec_to_usec_round(nsec):
238     """Round nanoseconds to microseconds"""
239     return (nsec + 500) // 10 ** 3
240
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py
--- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py
+++ b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py
@@ -183,6 +183,15 @@
                     "otel.instrumentation_library.version"
                 ] = span.instrumentation_info.version
 
+            if span.status is not None:
+                zipkin_span["tags"][
+                    "otel.status_code"
+                ] = span.status.canonical_code.value
+                if span.status.description is not None:
+                    zipkin_span["tags"][
+                        "otel.status_description"
+                    ] = span.status.description
+
             if context.trace_flags.sampled:
                 zipkin_span["debug"] = True

{"golden_diff": "diff --git a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py\n--- a/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py\n+++ b/exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py\n@@ -183,6 +183,15 @@\n                     \"otel.instrumentation_library.version\"\n                 ] = span.instrumentation_info.version\n \n+            if span.status is not None:\n+                zipkin_span[\"tags\"][\n+                    \"otel.status_code\"\n+                ] = span.status.canonical_code.value\n+                if span.status.description is not None:\n+                    zipkin_span[\"tags\"][\n+                        \"otel.status_description\"\n+                    ] = span.status.description\n+\n             if context.trace_flags.sampled:\n                 zipkin_span[\"debug\"] = True\n", "issue": "Zipkin exporter must map status\nAs per spec: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk_exporters/zipkin.md#status\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThis library allows to export tracing data to `Zipkin <https://zipkin.io/>`_.\n\nUsage\n-----\n\nThe **OpenTelemetry Zipkin Exporter** allows to export `OpenTelemetry`_ traces to `Zipkin`_.\nThis exporter always send traces to the configured Zipkin collector using HTTP.\n\n\n.. _Zipkin: https://zipkin.io/\n.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/\n.. _Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md#zipkin-exporter\n\n.. code:: python\n\n    from opentelemetry import trace\n    from opentelemetry.exporter import zipkin\n    from opentelemetry.sdk.trace import TracerProvider\n    from opentelemetry.sdk.trace.export import BatchExportSpanProcessor\n\n    trace.set_tracer_provider(TracerProvider())\n    tracer = trace.get_tracer(__name__)\n\n    # create a ZipkinSpanExporter\n    zipkin_exporter = zipkin.ZipkinSpanExporter(\n        service_name=\"my-helloworld-service\",\n        # optional:\n        # url=\"http://localhost:9411/api/v2/spans\",\n        # ipv4=\"\",\n        # ipv6=\"\",\n        # retry=False,\n    )\n\n    # Create a BatchExportSpanProcessor and add the exporter to it\n    span_processor = BatchExportSpanProcessor(zipkin_exporter)\n\n    # add to the tracer\n    trace.get_tracer_provider().add_span_processor(span_processor)\n\n    with tracer.start_as_current_span(\"foo\"):\n        print(\"Hello world!\")\n\nThe exporter supports endpoint configuration via the OTEL_EXPORTER_ZIPKIN_ENDPOINT environment variables as defined in the `Specification`_\n\nAPI\n---\n\"\"\"\n\nimport json\nimport logging\nimport os\nfrom typing import Optional, Sequence\nfrom urllib.parse import urlparse\n\nimport requests\n\nfrom opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult\nfrom opentelemetry.trace import Span, SpanContext, SpanKind\n\nDEFAULT_RETRY = False\nDEFAULT_URL = \"http://localhost:9411/api/v2/spans\"\nZIPKIN_HEADERS = {\"Content-Type\": \"application/json\"}\n\nSPAN_KIND_MAP = {\n    SpanKind.INTERNAL: None,\n    SpanKind.SERVER: \"SERVER\",\n    SpanKind.CLIENT: \"CLIENT\",\n    SpanKind.PRODUCER: \"PRODUCER\",\n    SpanKind.CONSUMER: \"CONSUMER\",\n}\n\nSUCCESS_STATUS_CODES = (200, 202)\n\nlogger = logging.getLogger(__name__)\n\n\nclass ZipkinSpanExporter(SpanExporter):\n    \"\"\"Zipkin span exporter for OpenTelemetry.\n\n    Args:\n        service_name: Service that logged an annotation in a trace.Classifier\n            when query for spans.\n        url: The Zipkin endpoint URL\n        ipv4: Primary IPv4 address associated with this connection.\n        ipv6: Primary IPv6 address associated with this connection.\n        retry: Set to True to configure the exporter to retry on failure.\n    \"\"\"\n\n    def __init__(\n        self,\n        service_name: str,\n        url: str = None,\n        ipv4: Optional[str] = None,\n        ipv6: Optional[str] = None,\n        retry: Optional[str] = DEFAULT_RETRY,\n    ):\n        self.service_name = service_name\n        if url is None:\n            self.url = os.environ.get(\n                \"OTEL_EXPORTER_ZIPKIN_ENDPOINT\", DEFAULT_URL\n            )\n        else:\n            self.url = url\n\n        self.port = urlparse(self.url).port\n\n        self.ipv4 = ipv4\n        self.ipv6 = ipv6\n        self.retry = retry\n\n    def export(self, spans: Sequence[Span]) -> SpanExportResult:\n        zipkin_spans = self._translate_to_zipkin(spans)\n        result = requests.post(\n            url=self.url, data=json.dumps(zipkin_spans), headers=ZIPKIN_HEADERS\n        )\n\n        if result.status_code not in SUCCESS_STATUS_CODES:\n            logger.error(\n                \"Traces cannot be uploaded; status code: %s, message %s\",\n                result.status_code,\n                result.text,\n            )\n\n            if self.retry:\n                return SpanExportResult.FAILURE\n            return SpanExportResult.FAILURE\n        return SpanExportResult.SUCCESS\n\n    def _translate_to_zipkin(self, spans: Sequence[Span]):\n\n        local_endpoint = {\"serviceName\": self.service_name, \"port\": self.port}\n\n        if self.ipv4 is not None:\n            local_endpoint[\"ipv4\"] = self.ipv4\n\n        if self.ipv6 is not None:\n            local_endpoint[\"ipv6\"] = self.ipv6\n\n        zipkin_spans = []\n        for span in spans:\n            context = span.get_context()\n            trace_id = context.trace_id\n            span_id = context.span_id\n\n            # Timestamp in zipkin spans is int of microseconds.\n            # see: https://zipkin.io/pages/instrumenting.html\n            start_timestamp_mus = _nsec_to_usec_round(span.start_time)\n            duration_mus = _nsec_to_usec_round(span.end_time - span.start_time)\n\n            zipkin_span = {\n                # Ensure left-zero-padding of traceId, spanId, parentId\n                \"traceId\": format(trace_id, \"032x\"),\n                \"id\": format(span_id, \"016x\"),\n                \"name\": span.name,\n                \"timestamp\": start_timestamp_mus,\n                \"duration\": duration_mus,\n                \"localEndpoint\": local_endpoint,\n                \"kind\": SPAN_KIND_MAP[span.kind],\n                \"tags\": _extract_tags_from_span(span),\n                \"annotations\": _extract_annotations_from_events(span.events),\n            }\n\n            if span.instrumentation_info is not None:\n                zipkin_span[\"tags\"][\n                    \"otel.instrumentation_library.name\"\n                ] = span.instrumentation_info.name\n                zipkin_span[\"tags\"][\n                    \"otel.instrumentation_library.version\"\n                ] = span.instrumentation_info.version\n\n            if context.trace_flags.sampled:\n                zipkin_span[\"debug\"] = True\n\n            if isinstance(span.parent, Span):\n                zipkin_span[\"parentId\"] = format(\n                    span.parent.get_context().span_id, \"016x\"\n                )\n            elif isinstance(span.parent, SpanContext):\n                zipkin_span[\"parentId\"] = format(span.parent.span_id, \"016x\")\n\n            zipkin_spans.append(zipkin_span)\n        return zipkin_spans\n\n    def shutdown(self) -> None:\n        pass\n\n\ndef _extract_tags_from_dict(tags_dict):\n    tags = {}\n    if not tags_dict:\n        return tags\n    for attribute_key, attribute_value in tags_dict.items():\n        if isinstance(attribute_value, (int, bool, float)):\n            value = str(attribute_value)\n        elif isinstance(attribute_value, str):\n            value = attribute_value[:128]\n        else:\n            logger.warning(\"Could not serialize tag %s\", attribute_key)\n            continue\n        tags[attribute_key] = value\n    return tags\n\n\ndef _extract_tags_from_span(span: Span):\n    tags = _extract_tags_from_dict(getattr(span, \"attributes\", None))\n    if span.resource:\n        tags.update(_extract_tags_from_dict(span.resource.attributes))\n    return tags\n\n\ndef _extract_annotations_from_events(events):\n    return (\n        [\n            {\"timestamp\": _nsec_to_usec_round(e.timestamp), \"value\": e.name}\n            for e in events\n        ]\n        if events\n        else None\n    )\n\n\ndef _nsec_to_usec_round(nsec):\n    \"\"\"Round nanoseconds to microseconds\"\"\"\n    return (nsec + 500) // 10 ** 3\n", "path": "exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nThis library allows to export tracing data to `Zipkin <https://zipkin.io/>`_.\n\nUsage\n-----\n\nThe **OpenTelemetry Zipkin Exporter** allows to export `OpenTelemetry`_ traces to `Zipkin`_.\nThis exporter always send traces to the configured Zipkin collector using HTTP.\n\n\n.. _Zipkin: https://zipkin.io/\n.. _OpenTelemetry: https://github.com/open-telemetry/opentelemetry-python/\n.. _Specification: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/sdk-environment-variables.md#zipkin-exporter\n\n.. code:: python\n\n    from opentelemetry import trace\n    from opentelemetry.exporter import zipkin\n    from opentelemetry.sdk.trace import TracerProvider\n    from opentelemetry.sdk.trace.export import BatchExportSpanProcessor\n\n    trace.set_tracer_provider(TracerProvider())\n    tracer = trace.get_tracer(__name__)\n\n    # create a ZipkinSpanExporter\n    zipkin_exporter = zipkin.ZipkinSpanExporter(\n        service_name=\"my-helloworld-service\",\n        # optional:\n        # url=\"http://localhost:9411/api/v2/spans\",\n        # ipv4=\"\",\n        # ipv6=\"\",\n        # retry=False,\n    )\n\n    # Create a BatchExportSpanProcessor and add the exporter to it\n    span_processor = BatchExportSpanProcessor(zipkin_exporter)\n\n    # add to the tracer\n    trace.get_tracer_provider().add_span_processor(span_processor)\n\n    with tracer.start_as_current_span(\"foo\"):\n        print(\"Hello world!\")\n\nThe exporter supports endpoint configuration via the OTEL_EXPORTER_ZIPKIN_ENDPOINT environment variables as defined in the `Specification`_\n\nAPI\n---\n\"\"\"\n\nimport json\nimport logging\nimport os\nfrom typing import Optional, Sequence\nfrom urllib.parse import urlparse\n\nimport requests\n\nfrom opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult\nfrom opentelemetry.trace import Span, SpanContext, SpanKind\n\nDEFAULT_RETRY = False\nDEFAULT_URL = \"http://localhost:9411/api/v2/spans\"\nZIPKIN_HEADERS = {\"Content-Type\": \"application/json\"}\n\nSPAN_KIND_MAP = {\n    SpanKind.INTERNAL: None,\n    SpanKind.SERVER: \"SERVER\",\n    SpanKind.CLIENT: \"CLIENT\",\n    SpanKind.PRODUCER: \"PRODUCER\",\n    SpanKind.CONSUMER: \"CONSUMER\",\n}\n\nSUCCESS_STATUS_CODES = (200, 202)\n\nlogger = logging.getLogger(__name__)\n\n\nclass ZipkinSpanExporter(SpanExporter):\n    \"\"\"Zipkin span exporter for OpenTelemetry.\n\n    Args:\n        service_name: Service that logged an annotation in a trace.Classifier\n            when query for spans.\n        url: The Zipkin endpoint URL\n        ipv4: Primary IPv4 address associated with this connection.\n        ipv6: Primary IPv6 address associated with this connection.\n        retry: Set to True to configure the exporter to retry on failure.\n    \"\"\"\n\n    def __init__(\n        self,\n        service_name: str,\n        url: str = None,\n        ipv4: Optional[str] = None,\n        ipv6: Optional[str] = None,\n        retry: Optional[str] = DEFAULT_RETRY,\n    ):\n        self.service_name = service_name\n        if url is None:\n            self.url = os.environ.get(\n                \"OTEL_EXPORTER_ZIPKIN_ENDPOINT\", DEFAULT_URL\n            )\n        else:\n            self.url = url\n\n        self.port = urlparse(self.url).port\n\n        self.ipv4 = ipv4\n        self.ipv6 = ipv6\n        self.retry = retry\n\n    def export(self, spans: Sequence[Span]) -> SpanExportResult:\n        zipkin_spans = self._translate_to_zipkin(spans)\n        result = requests.post(\n            url=self.url, data=json.dumps(zipkin_spans), headers=ZIPKIN_HEADERS\n        )\n\n        if result.status_code not in SUCCESS_STATUS_CODES:\n            logger.error(\n                \"Traces cannot be uploaded; status code: %s, message %s\",\n                result.status_code,\n                result.text,\n            )\n\n            if self.retry:\n                return SpanExportResult.FAILURE\n            return SpanExportResult.FAILURE\n        return SpanExportResult.SUCCESS\n\n    def _translate_to_zipkin(self, spans: Sequence[Span]):\n\n        local_endpoint = {\"serviceName\": self.service_name, \"port\": self.port}\n\n        if self.ipv4 is not None:\n            local_endpoint[\"ipv4\"] = self.ipv4\n\n        if self.ipv6 is not None:\n            local_endpoint[\"ipv6\"] = self.ipv6\n\n        zipkin_spans = []\n        for span in spans:\n            context = span.get_context()\n            trace_id = context.trace_id\n            span_id = context.span_id\n\n            # Timestamp in zipkin spans is int of microseconds.\n            # see: https://zipkin.io/pages/instrumenting.html\n            start_timestamp_mus = _nsec_to_usec_round(span.start_time)\n            duration_mus = _nsec_to_usec_round(span.end_time - span.start_time)\n\n            zipkin_span = {\n                # Ensure left-zero-padding of traceId, spanId, parentId\n                \"traceId\": format(trace_id, \"032x\"),\n                \"id\": format(span_id, \"016x\"),\n                \"name\": span.name,\n                \"timestamp\": start_timestamp_mus,\n                \"duration\": duration_mus,\n                \"localEndpoint\": local_endpoint,\n                \"kind\": SPAN_KIND_MAP[span.kind],\n                \"tags\": _extract_tags_from_span(span),\n                \"annotations\": _extract_annotations_from_events(span.events),\n            }\n\n            if span.instrumentation_info is not None:\n                zipkin_span[\"tags\"][\n                    \"otel.instrumentation_library.name\"\n                ] = span.instrumentation_info.name\n                zipkin_span[\"tags\"][\n                    \"otel.instrumentation_library.version\"\n                ] = span.instrumentation_info.version\n\n            if span.status is not None:\n                zipkin_span[\"tags\"][\n                    \"otel.status_code\"\n                ] = span.status.canonical_code.value\n                if span.status.description is not None:\n                    zipkin_span[\"tags\"][\n                        \"otel.status_description\"\n                    ] = span.status.description\n\n            if context.trace_flags.sampled:\n                zipkin_span[\"debug\"] = True\n\n            if isinstance(span.parent, Span):\n                zipkin_span[\"parentId\"] = format(\n                    span.parent.get_context().span_id, \"016x\"\n                )\n            elif isinstance(span.parent, SpanContext):\n                zipkin_span[\"parentId\"] = format(span.parent.span_id, \"016x\")\n\n            zipkin_spans.append(zipkin_span)\n        return zipkin_spans\n\n    def shutdown(self) -> None:\n        pass\n\n\ndef _extract_tags_from_dict(tags_dict):\n    tags = {}\n    if not tags_dict:\n        return tags\n    for attribute_key, attribute_value in tags_dict.items():\n        if isinstance(attribute_value, (int, bool, float)):\n            value = str(attribute_value)\n        elif isinstance(attribute_value, str):\n            value = attribute_value[:128]\n        else:\n            logger.warning(\"Could not serialize tag %s\", attribute_key)\n            continue\n        tags[attribute_key] = value\n    return tags\n\n\ndef _extract_tags_from_span(span: Span):\n    tags = _extract_tags_from_dict(getattr(span, \"attributes\", None))\n    if span.resource:\n        tags.update(_extract_tags_from_dict(span.resource.attributes))\n    return tags\n\n\ndef _extract_annotations_from_events(events):\n    return (\n        [\n            {\"timestamp\": _nsec_to_usec_round(e.timestamp), \"value\": e.name}\n            for e in events\n        ]\n        if events\n        else None\n    )\n\n\ndef _nsec_to_usec_round(nsec):\n    \"\"\"Round nanoseconds to microseconds\"\"\"\n    return (nsec + 500) // 10 ** 3\n", "path": "exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py"}]}