We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
datadog: honor DD environment variables
There's a few environment variables that are not honored by the exporter currently. These are:

* DD_AGENT_HOST, for the agent hostname
* DATADOG_SERVICE_NAME, DD_SERVICE_NAME, DD_SERVICE for the datadog service name.

There is a more comprehensive list, as outlined here:

https://docs.datadoghq.com/tracing/setup/python/#configuration

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.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 import logging
16 import os
17 from urllib.parse import urlparse
18 
19 from ddtrace.ext import SpanTypes as DatadogSpanTypes
20 from ddtrace.internal.writer import AgentWriter
21 from ddtrace.span import Span as DatadogSpan
22 
23 import opentelemetry.trace as trace_api
24 from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
25 from opentelemetry.trace.status import StatusCanonicalCode
26 
27 # pylint:disable=relative-beyond-top-level
28 from .constants import DD_ORIGIN, SAMPLE_RATE_METRIC_KEY
29 
30 logger = logging.getLogger(__name__)
31 
32 
33 DEFAULT_AGENT_URL = "http://localhost:8126"
34 _INSTRUMENTATION_SPAN_TYPES = {
35     "opentelemetry.ext.aiohttp-client": DatadogSpanTypes.HTTP,
36     "opentelemetry.ext.asgi": DatadogSpanTypes.WEB,
37     "opentelemetry.ext.dbapi": DatadogSpanTypes.SQL,
38     "opentelemetry.ext.django": DatadogSpanTypes.WEB,
39     "opentelemetry.ext.flask": DatadogSpanTypes.WEB,
40     "opentelemetry.ext.grpc": DatadogSpanTypes.GRPC,
41     "opentelemetry.ext.jinja2": DatadogSpanTypes.TEMPLATE,
42     "opentelemetry.ext.mysql": DatadogSpanTypes.SQL,
43     "opentelemetry.ext.psycopg2": DatadogSpanTypes.SQL,
44     "opentelemetry.ext.pymemcache": DatadogSpanTypes.CACHE,
45     "opentelemetry.ext.pymongo": DatadogSpanTypes.MONGODB,
46     "opentelemetry.ext.pymysql": DatadogSpanTypes.SQL,
47     "opentelemetry.ext.redis": DatadogSpanTypes.REDIS,
48     "opentelemetry.ext.requests": DatadogSpanTypes.HTTP,
49     "opentelemetry.ext.sqlalchemy": DatadogSpanTypes.SQL,
50     "opentelemetry.ext.wsgi": DatadogSpanTypes.WEB,
51 }
52 
53 
54 class DatadogSpanExporter(SpanExporter):
55     """Datadog span exporter for OpenTelemetry.
56 
57     Args:
58         agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable
59         service: The service to be used for the application or use ``DD_SERVICE`` environment variable
60     """
61 
62     def __init__(self, agent_url=None, service=None):
63         self.agent_url = (
64             agent_url
65             if agent_url
66             else os.environ.get("DD_TRACE_AGENT_URL", DEFAULT_AGENT_URL)
67         )
68         self.service = service if service else os.environ.get("DD_SERVICE")
69         self._agent_writer = None
70 
71     @property
72     def agent_writer(self):
73         if self._agent_writer is None:
74             url_parsed = urlparse(self.agent_url)
75             if url_parsed.scheme in ("http", "https"):
76                 self._agent_writer = AgentWriter(
77                     hostname=url_parsed.hostname,
78                     port=url_parsed.port,
79                     https=url_parsed.scheme == "https",
80                 )
81             elif url_parsed.scheme == "unix":
82                 self._agent_writer = AgentWriter(uds_path=url_parsed.path)
83             else:
84                 raise ValueError(
85                     "Unknown scheme `%s` for agent URL" % url_parsed.scheme
86                 )
87         return self._agent_writer
88 
89     def export(self, spans):
90         datadog_spans = self._translate_to_datadog(spans)
91 
92         self.agent_writer.write(spans=datadog_spans)
93 
94         return SpanExportResult.SUCCESS
95 
96     def shutdown(self):
97         if self.agent_writer.started:
98             self.agent_writer.stop()
99             self.agent_writer.join(self.agent_writer.exit_timeout)
100 
101     def _translate_to_datadog(self, spans):
102         datadog_spans = []
103 
104         for span in spans:
105             trace_id, parent_id, span_id = _get_trace_ids(span)
106 
107             # datadog Span is initialized with a reference to the tracer which is
108             # used to record the span when it is finished. We can skip ignore this
109             # because we are not calling the finish method and explictly set the
110             # duration.
111             tracer = None
112 
113             datadog_span = DatadogSpan(
114                 tracer,
115                 _get_span_name(span),
116                 service=self.service,
117                 resource=_get_resource(span),
118                 span_type=_get_span_type(span),
119                 trace_id=trace_id,
120                 span_id=span_id,
121                 parent_id=parent_id,
122             )
123             datadog_span.start_ns = span.start_time
124             datadog_span.duration_ns = span.end_time - span.start_time
125 
126             if span.status.canonical_code is not StatusCanonicalCode.OK:
127                 datadog_span.error = 1
128                 if span.status.description:
129                     exc_type, exc_val = _get_exc_info(span)
130                     # no mapping for error.stack since traceback not recorded
131                     datadog_span.set_tag("error.msg", exc_val)
132                     datadog_span.set_tag("error.type", exc_type)
133 
134             datadog_span.set_tags(span.attributes)
135 
136             # add origin to root span
137             origin = _get_origin(span)
138             if origin and parent_id == 0:
139                 datadog_span.set_tag(DD_ORIGIN, origin)
140 
141             sampling_rate = _get_sampling_rate(span)
142             if sampling_rate is not None:
143                 datadog_span.set_metric(SAMPLE_RATE_METRIC_KEY, sampling_rate)
144 
145             # span events and span links are not supported
146 
147             datadog_spans.append(datadog_span)
148 
149         return datadog_spans
150 
151 
152 def _get_trace_ids(span):
153     """Extract tracer ids from span"""
154     ctx = span.get_context()
155     trace_id = ctx.trace_id
156     span_id = ctx.span_id
157 
158     if isinstance(span.parent, trace_api.Span):
159         parent_id = span.parent.get_context().span_id
160     elif isinstance(span.parent, trace_api.SpanContext):
161         parent_id = span.parent.span_id
162     else:
163         parent_id = 0
164 
165     trace_id = _convert_trace_id_uint64(trace_id)
166 
167     return trace_id, parent_id, span_id
168 
169 
170 def _convert_trace_id_uint64(otel_id):
171     """Convert 128-bit int used for trace_id to 64-bit unsigned int"""
172     return otel_id & 0xFFFFFFFFFFFFFFFF
173 
174 
175 def _get_span_name(span):
176     """Get span name by using instrumentation and kind while backing off to
177     span.name
178     """
179     instrumentation_name = (
180         span.instrumentation_info.name if span.instrumentation_info else None
181     )
182     span_kind_name = span.kind.name if span.kind else None
183     name = (
184         "{}.{}".format(instrumentation_name, span_kind_name)
185         if instrumentation_name and span_kind_name
186         else span.name
187     )
188     return name
189 
190 
191 def _get_resource(span):
192     """Get resource name for span"""
193     if "http.method" in span.attributes:
194         route = span.attributes.get("http.route")
195         return (
196             span.attributes["http.method"] + " " + route
197             if route
198             else span.attributes["http.method"]
199         )
200 
201     return span.name
202 
203 
204 def _get_span_type(span):
205     """Get Datadog span type"""
206     instrumentation_name = (
207         span.instrumentation_info.name if span.instrumentation_info else None
208     )
209     span_type = _INSTRUMENTATION_SPAN_TYPES.get(instrumentation_name)
210     return span_type
211 
212 
213 def _get_exc_info(span):
214     """Parse span status description for exception type and value"""
215     exc_type, exc_val = span.status.description.split(":", 1)
216     return exc_type, exc_val.strip()
217 
218 
219 def _get_origin(span):
220     ctx = span.get_context()
221     origin = ctx.trace_state.get(DD_ORIGIN)
222     return origin
223 
224 
225 def _get_sampling_rate(span):
226     ctx = span.get_context()
227     return (
228         span.sampler.rate
229         if ctx.trace_flags.sampled
230         and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)
231         else None
232     )
233
```

Path: `ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py`
Content:
```
1 DD_ORIGIN = "_dd_origin"
2 AUTO_REJECT = 0
3 AUTO_KEEP = 1
4 USER_KEEP = 2
5 SAMPLE_RATE_METRIC_KEY = "_sample_rate"
6 SAMPLING_PRIORITY_KEY = "_sampling_priority_v1"
7
```
--- 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/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py
--- a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py
+++ b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py
@@ -4,3 +4,5 @@
 USER_KEEP = 2
 SAMPLE_RATE_METRIC_KEY = "_sample_rate"
 SAMPLING_PRIORITY_KEY = "_sampling_priority_v1"
+ENV_KEY = "env"
+VERSION_KEY = "version"
diff --git a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
--- a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
+++ b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py
@@ -25,7 +25,7 @@
 from opentelemetry.trace.status import StatusCanonicalCode
 
 # pylint:disable=relative-beyond-top-level
-from .constants import DD_ORIGIN, SAMPLE_RATE_METRIC_KEY
+from .constants import DD_ORIGIN, ENV_KEY, SAMPLE_RATE_METRIC_KEY, VERSION_KEY
 
 logger = logging.getLogger(__name__)
 
@@ -56,16 +56,24 @@
 
     Args:
         agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable
-        service: The service to be used for the application or use ``DD_SERVICE`` environment variable
+        service: The service name to be used for the application or use ``DD_SERVICE`` environment variable
+        env: Set the application’s environment or use ``DD_ENV`` environment variable
+        version: Set the application’s version or use ``DD_VERSION`` environment variable
+        tags: A list of default tags to be added to every span or use ``DD_TAGS`` environment variable
     """
 
-    def __init__(self, agent_url=None, service=None):
+    def __init__(
+        self, agent_url=None, service=None, env=None, version=None, tags=None
+    ):
         self.agent_url = (
             agent_url
             if agent_url
             else os.environ.get("DD_TRACE_AGENT_URL", DEFAULT_AGENT_URL)
         )
-        self.service = service if service else os.environ.get("DD_SERVICE")
+        self.service = service or os.environ.get("DD_SERVICE")
+        self.env = env or os.environ.get("DD_ENV")
+        self.version = version or os.environ.get("DD_VERSION")
+        self.tags = _parse_tags_str(tags or os.environ.get("DD_TAGS"))
         self._agent_writer = None
 
     @property
@@ -133,6 +141,17 @@
 
             datadog_span.set_tags(span.attributes)
 
+            # add configured env tag
+            if self.env is not None:
+                datadog_span.set_tag(ENV_KEY, self.env)
+
+            # add configured application version tag to only root span
+            if self.version is not None and parent_id == 0:
+                datadog_span.set_tag(VERSION_KEY, self.version)
+
+            # add configured global tags
+            datadog_span.set_tags(self.tags)
+
             # add origin to root span
             origin = _get_origin(span)
             if origin and parent_id == 0:
@@ -230,3 +249,35 @@
         and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)
         else None
     )
+
+
+def _parse_tags_str(tags_str):
+    """Parse a string of tags typically provided via environment variables.
+
+    The expected string is of the form::
+        "key1:value1,key2:value2"
+
+    :param tags_str: A string of the above form to parse tags from.
+    :return: A dict containing the tags that were parsed.
+    """
+    parsed_tags = {}
+    if not tags_str:
+        return parsed_tags
+
+    for tag in tags_str.split(","):
+        try:
+            key, value = tag.split(":", 1)
+
+            # Validate the tag
+            if key == "" or value == "" or value.endswith(":"):
+                raise ValueError
+        except ValueError:
+            logger.error(
+                "Malformed tag in tag pair '%s' from tag string '%s'.",
+                tag,
+                tags_str,
+            )
+        else:
+            parsed_tags[key] = value
+
+    return parsed_tags

{"golden_diff": "diff --git a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py\n--- a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py\n+++ b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py\n@@ -4,3 +4,5 @@\n USER_KEEP = 2\n SAMPLE_RATE_METRIC_KEY = \"_sample_rate\"\n SAMPLING_PRIORITY_KEY = \"_sampling_priority_v1\"\n+ENV_KEY = \"env\"\n+VERSION_KEY = \"version\"\ndiff --git a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py\n--- a/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py\n+++ b/ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py\n@@ -25,7 +25,7 @@\n from opentelemetry.trace.status import StatusCanonicalCode\n \n # pylint:disable=relative-beyond-top-level\n-from .constants import DD_ORIGIN, SAMPLE_RATE_METRIC_KEY\n+from .constants import DD_ORIGIN, ENV_KEY, SAMPLE_RATE_METRIC_KEY, VERSION_KEY\n \n logger = logging.getLogger(__name__)\n \n@@ -56,16 +56,24 @@\n \n     Args:\n         agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable\n-        service: The service to be used for the application or use ``DD_SERVICE`` environment variable\n+        service: The service name to be used for the application or use ``DD_SERVICE`` environment variable\n+        env: Set the application\u2019s environment or use ``DD_ENV`` environment variable\n+        version: Set the application\u2019s version or use ``DD_VERSION`` environment variable\n+        tags: A list of default tags to be added to every span or use ``DD_TAGS`` environment variable\n     \"\"\"\n \n-    def __init__(self, agent_url=None, service=None):\n+    def __init__(\n+        self, agent_url=None, service=None, env=None, version=None, tags=None\n+    ):\n         self.agent_url = (\n             agent_url\n             if agent_url\n             else os.environ.get(\"DD_TRACE_AGENT_URL\", DEFAULT_AGENT_URL)\n         )\n-        self.service = service if service else os.environ.get(\"DD_SERVICE\")\n+        self.service = service or os.environ.get(\"DD_SERVICE\")\n+        self.env = env or os.environ.get(\"DD_ENV\")\n+        self.version = version or os.environ.get(\"DD_VERSION\")\n+        self.tags = _parse_tags_str(tags or os.environ.get(\"DD_TAGS\"))\n         self._agent_writer = None\n \n     @property\n@@ -133,6 +141,17 @@\n \n             datadog_span.set_tags(span.attributes)\n \n+            # add configured env tag\n+            if self.env is not None:\n+                datadog_span.set_tag(ENV_KEY, self.env)\n+\n+            # add configured application version tag to only root span\n+            if self.version is not None and parent_id == 0:\n+                datadog_span.set_tag(VERSION_KEY, self.version)\n+\n+            # add configured global tags\n+            datadog_span.set_tags(self.tags)\n+\n             # add origin to root span\n             origin = _get_origin(span)\n             if origin and parent_id == 0:\n@@ -230,3 +249,35 @@\n         and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)\n         else None\n     )\n+\n+\n+def _parse_tags_str(tags_str):\n+    \"\"\"Parse a string of tags typically provided via environment variables.\n+\n+    The expected string is of the form::\n+        \"key1:value1,key2:value2\"\n+\n+    :param tags_str: A string of the above form to parse tags from.\n+    :return: A dict containing the tags that were parsed.\n+    \"\"\"\n+    parsed_tags = {}\n+    if not tags_str:\n+        return parsed_tags\n+\n+    for tag in tags_str.split(\",\"):\n+        try:\n+            key, value = tag.split(\":\", 1)\n+\n+            # Validate the tag\n+            if key == \"\" or value == \"\" or value.endswith(\":\"):\n+                raise ValueError\n+        except ValueError:\n+            logger.error(\n+                \"Malformed tag in tag pair '%s' from tag string '%s'.\",\n+                tag,\n+                tags_str,\n+            )\n+        else:\n+            parsed_tags[key] = value\n+\n+    return parsed_tags\n", "issue": "datadog: honor DD environment variables\nThere's a few environment variables that are not honored by the exporter currently. These are:\r\n\r\n* DD_AGENT_HOST, for the agent hostname\r\n* DATADOG_SERVICE_NAME, DD_SERVICE_NAME, DD_SERVICE for the datadog service name.\r\n\r\nThere is a more comprehensive list, as outlined here:\r\n\r\nhttps://docs.datadoghq.com/tracing/setup/python/#configuration\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\nimport logging\nimport os\nfrom urllib.parse import urlparse\n\nfrom ddtrace.ext import SpanTypes as DatadogSpanTypes\nfrom ddtrace.internal.writer import AgentWriter\nfrom ddtrace.span import Span as DatadogSpan\n\nimport opentelemetry.trace as trace_api\nfrom opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult\nfrom opentelemetry.trace.status import StatusCanonicalCode\n\n# pylint:disable=relative-beyond-top-level\nfrom .constants import DD_ORIGIN, SAMPLE_RATE_METRIC_KEY\n\nlogger = logging.getLogger(__name__)\n\n\nDEFAULT_AGENT_URL = \"http://localhost:8126\"\n_INSTRUMENTATION_SPAN_TYPES = {\n    \"opentelemetry.ext.aiohttp-client\": DatadogSpanTypes.HTTP,\n    \"opentelemetry.ext.asgi\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.dbapi\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.django\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.flask\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.grpc\": DatadogSpanTypes.GRPC,\n    \"opentelemetry.ext.jinja2\": DatadogSpanTypes.TEMPLATE,\n    \"opentelemetry.ext.mysql\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.psycopg2\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.pymemcache\": DatadogSpanTypes.CACHE,\n    \"opentelemetry.ext.pymongo\": DatadogSpanTypes.MONGODB,\n    \"opentelemetry.ext.pymysql\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.redis\": DatadogSpanTypes.REDIS,\n    \"opentelemetry.ext.requests\": DatadogSpanTypes.HTTP,\n    \"opentelemetry.ext.sqlalchemy\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.wsgi\": DatadogSpanTypes.WEB,\n}\n\n\nclass DatadogSpanExporter(SpanExporter):\n    \"\"\"Datadog span exporter for OpenTelemetry.\n\n    Args:\n        agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable\n        service: The service to be used for the application or use ``DD_SERVICE`` environment variable\n    \"\"\"\n\n    def __init__(self, agent_url=None, service=None):\n        self.agent_url = (\n            agent_url\n            if agent_url\n            else os.environ.get(\"DD_TRACE_AGENT_URL\", DEFAULT_AGENT_URL)\n        )\n        self.service = service if service else os.environ.get(\"DD_SERVICE\")\n        self._agent_writer = None\n\n    @property\n    def agent_writer(self):\n        if self._agent_writer is None:\n            url_parsed = urlparse(self.agent_url)\n            if url_parsed.scheme in (\"http\", \"https\"):\n                self._agent_writer = AgentWriter(\n                    hostname=url_parsed.hostname,\n                    port=url_parsed.port,\n                    https=url_parsed.scheme == \"https\",\n                )\n            elif url_parsed.scheme == \"unix\":\n                self._agent_writer = AgentWriter(uds_path=url_parsed.path)\n            else:\n                raise ValueError(\n                    \"Unknown scheme `%s` for agent URL\" % url_parsed.scheme\n                )\n        return self._agent_writer\n\n    def export(self, spans):\n        datadog_spans = self._translate_to_datadog(spans)\n\n        self.agent_writer.write(spans=datadog_spans)\n\n        return SpanExportResult.SUCCESS\n\n    def shutdown(self):\n        if self.agent_writer.started:\n            self.agent_writer.stop()\n            self.agent_writer.join(self.agent_writer.exit_timeout)\n\n    def _translate_to_datadog(self, spans):\n        datadog_spans = []\n\n        for span in spans:\n            trace_id, parent_id, span_id = _get_trace_ids(span)\n\n            # datadog Span is initialized with a reference to the tracer which is\n            # used to record the span when it is finished. We can skip ignore this\n            # because we are not calling the finish method and explictly set the\n            # duration.\n            tracer = None\n\n            datadog_span = DatadogSpan(\n                tracer,\n                _get_span_name(span),\n                service=self.service,\n                resource=_get_resource(span),\n                span_type=_get_span_type(span),\n                trace_id=trace_id,\n                span_id=span_id,\n                parent_id=parent_id,\n            )\n            datadog_span.start_ns = span.start_time\n            datadog_span.duration_ns = span.end_time - span.start_time\n\n            if span.status.canonical_code is not StatusCanonicalCode.OK:\n                datadog_span.error = 1\n                if span.status.description:\n                    exc_type, exc_val = _get_exc_info(span)\n                    # no mapping for error.stack since traceback not recorded\n                    datadog_span.set_tag(\"error.msg\", exc_val)\n                    datadog_span.set_tag(\"error.type\", exc_type)\n\n            datadog_span.set_tags(span.attributes)\n\n            # add origin to root span\n            origin = _get_origin(span)\n            if origin and parent_id == 0:\n                datadog_span.set_tag(DD_ORIGIN, origin)\n\n            sampling_rate = _get_sampling_rate(span)\n            if sampling_rate is not None:\n                datadog_span.set_metric(SAMPLE_RATE_METRIC_KEY, sampling_rate)\n\n            # span events and span links are not supported\n\n            datadog_spans.append(datadog_span)\n\n        return datadog_spans\n\n\ndef _get_trace_ids(span):\n    \"\"\"Extract tracer ids from span\"\"\"\n    ctx = span.get_context()\n    trace_id = ctx.trace_id\n    span_id = ctx.span_id\n\n    if isinstance(span.parent, trace_api.Span):\n        parent_id = span.parent.get_context().span_id\n    elif isinstance(span.parent, trace_api.SpanContext):\n        parent_id = span.parent.span_id\n    else:\n        parent_id = 0\n\n    trace_id = _convert_trace_id_uint64(trace_id)\n\n    return trace_id, parent_id, span_id\n\n\ndef _convert_trace_id_uint64(otel_id):\n    \"\"\"Convert 128-bit int used for trace_id to 64-bit unsigned int\"\"\"\n    return otel_id & 0xFFFFFFFFFFFFFFFF\n\n\ndef _get_span_name(span):\n    \"\"\"Get span name by using instrumentation and kind while backing off to\n    span.name\n    \"\"\"\n    instrumentation_name = (\n        span.instrumentation_info.name if span.instrumentation_info else None\n    )\n    span_kind_name = span.kind.name if span.kind else None\n    name = (\n        \"{}.{}\".format(instrumentation_name, span_kind_name)\n        if instrumentation_name and span_kind_name\n        else span.name\n    )\n    return name\n\n\ndef _get_resource(span):\n    \"\"\"Get resource name for span\"\"\"\n    if \"http.method\" in span.attributes:\n        route = span.attributes.get(\"http.route\")\n        return (\n            span.attributes[\"http.method\"] + \" \" + route\n            if route\n            else span.attributes[\"http.method\"]\n        )\n\n    return span.name\n\n\ndef _get_span_type(span):\n    \"\"\"Get Datadog span type\"\"\"\n    instrumentation_name = (\n        span.instrumentation_info.name if span.instrumentation_info else None\n    )\n    span_type = _INSTRUMENTATION_SPAN_TYPES.get(instrumentation_name)\n    return span_type\n\n\ndef _get_exc_info(span):\n    \"\"\"Parse span status description for exception type and value\"\"\"\n    exc_type, exc_val = span.status.description.split(\":\", 1)\n    return exc_type, exc_val.strip()\n\n\ndef _get_origin(span):\n    ctx = span.get_context()\n    origin = ctx.trace_state.get(DD_ORIGIN)\n    return origin\n\n\ndef _get_sampling_rate(span):\n    ctx = span.get_context()\n    return (\n        span.sampler.rate\n        if ctx.trace_flags.sampled\n        and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)\n        else None\n    )\n", "path": "ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py"}, {"content": "DD_ORIGIN = \"_dd_origin\"\nAUTO_REJECT = 0\nAUTO_KEEP = 1\nUSER_KEEP = 2\nSAMPLE_RATE_METRIC_KEY = \"_sample_rate\"\nSAMPLING_PRIORITY_KEY = \"_sampling_priority_v1\"\n", "path": "ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.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\nimport logging\nimport os\nfrom urllib.parse import urlparse\n\nfrom ddtrace.ext import SpanTypes as DatadogSpanTypes\nfrom ddtrace.internal.writer import AgentWriter\nfrom ddtrace.span import Span as DatadogSpan\n\nimport opentelemetry.trace as trace_api\nfrom opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult\nfrom opentelemetry.trace.status import StatusCanonicalCode\n\n# pylint:disable=relative-beyond-top-level\nfrom .constants import DD_ORIGIN, ENV_KEY, SAMPLE_RATE_METRIC_KEY, VERSION_KEY\n\nlogger = logging.getLogger(__name__)\n\n\nDEFAULT_AGENT_URL = \"http://localhost:8126\"\n_INSTRUMENTATION_SPAN_TYPES = {\n    \"opentelemetry.ext.aiohttp-client\": DatadogSpanTypes.HTTP,\n    \"opentelemetry.ext.asgi\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.dbapi\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.django\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.flask\": DatadogSpanTypes.WEB,\n    \"opentelemetry.ext.grpc\": DatadogSpanTypes.GRPC,\n    \"opentelemetry.ext.jinja2\": DatadogSpanTypes.TEMPLATE,\n    \"opentelemetry.ext.mysql\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.psycopg2\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.pymemcache\": DatadogSpanTypes.CACHE,\n    \"opentelemetry.ext.pymongo\": DatadogSpanTypes.MONGODB,\n    \"opentelemetry.ext.pymysql\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.redis\": DatadogSpanTypes.REDIS,\n    \"opentelemetry.ext.requests\": DatadogSpanTypes.HTTP,\n    \"opentelemetry.ext.sqlalchemy\": DatadogSpanTypes.SQL,\n    \"opentelemetry.ext.wsgi\": DatadogSpanTypes.WEB,\n}\n\n\nclass DatadogSpanExporter(SpanExporter):\n    \"\"\"Datadog span exporter for OpenTelemetry.\n\n    Args:\n        agent_url: The url of the Datadog Agent or use ``DD_TRACE_AGENT_URL`` environment variable\n        service: The service name to be used for the application or use ``DD_SERVICE`` environment variable\n        env: Set the application\u2019s environment or use ``DD_ENV`` environment variable\n        version: Set the application\u2019s version or use ``DD_VERSION`` environment variable\n        tags: A list of default tags to be added to every span or use ``DD_TAGS`` environment variable\n    \"\"\"\n\n    def __init__(\n        self, agent_url=None, service=None, env=None, version=None, tags=None\n    ):\n        self.agent_url = (\n            agent_url\n            if agent_url\n            else os.environ.get(\"DD_TRACE_AGENT_URL\", DEFAULT_AGENT_URL)\n        )\n        self.service = service or os.environ.get(\"DD_SERVICE\")\n        self.env = env or os.environ.get(\"DD_ENV\")\n        self.version = version or os.environ.get(\"DD_VERSION\")\n        self.tags = _parse_tags_str(tags or os.environ.get(\"DD_TAGS\"))\n        self._agent_writer = None\n\n    @property\n    def agent_writer(self):\n        if self._agent_writer is None:\n            url_parsed = urlparse(self.agent_url)\n            if url_parsed.scheme in (\"http\", \"https\"):\n                self._agent_writer = AgentWriter(\n                    hostname=url_parsed.hostname,\n                    port=url_parsed.port,\n                    https=url_parsed.scheme == \"https\",\n                )\n            elif url_parsed.scheme == \"unix\":\n                self._agent_writer = AgentWriter(uds_path=url_parsed.path)\n            else:\n                raise ValueError(\n                    \"Unknown scheme `%s` for agent URL\" % url_parsed.scheme\n                )\n        return self._agent_writer\n\n    def export(self, spans):\n        datadog_spans = self._translate_to_datadog(spans)\n\n        self.agent_writer.write(spans=datadog_spans)\n\n        return SpanExportResult.SUCCESS\n\n    def shutdown(self):\n        if self.agent_writer.started:\n            self.agent_writer.stop()\n            self.agent_writer.join(self.agent_writer.exit_timeout)\n\n    def _translate_to_datadog(self, spans):\n        datadog_spans = []\n\n        for span in spans:\n            trace_id, parent_id, span_id = _get_trace_ids(span)\n\n            # datadog Span is initialized with a reference to the tracer which is\n            # used to record the span when it is finished. We can skip ignore this\n            # because we are not calling the finish method and explictly set the\n            # duration.\n            tracer = None\n\n            datadog_span = DatadogSpan(\n                tracer,\n                _get_span_name(span),\n                service=self.service,\n                resource=_get_resource(span),\n                span_type=_get_span_type(span),\n                trace_id=trace_id,\n                span_id=span_id,\n                parent_id=parent_id,\n            )\n            datadog_span.start_ns = span.start_time\n            datadog_span.duration_ns = span.end_time - span.start_time\n\n            if span.status.canonical_code is not StatusCanonicalCode.OK:\n                datadog_span.error = 1\n                if span.status.description:\n                    exc_type, exc_val = _get_exc_info(span)\n                    # no mapping for error.stack since traceback not recorded\n                    datadog_span.set_tag(\"error.msg\", exc_val)\n                    datadog_span.set_tag(\"error.type\", exc_type)\n\n            datadog_span.set_tags(span.attributes)\n\n            # add configured env tag\n            if self.env is not None:\n                datadog_span.set_tag(ENV_KEY, self.env)\n\n            # add configured application version tag to only root span\n            if self.version is not None and parent_id == 0:\n                datadog_span.set_tag(VERSION_KEY, self.version)\n\n            # add configured global tags\n            datadog_span.set_tags(self.tags)\n\n            # add origin to root span\n            origin = _get_origin(span)\n            if origin and parent_id == 0:\n                datadog_span.set_tag(DD_ORIGIN, origin)\n\n            sampling_rate = _get_sampling_rate(span)\n            if sampling_rate is not None:\n                datadog_span.set_metric(SAMPLE_RATE_METRIC_KEY, sampling_rate)\n\n            # span events and span links are not supported\n\n            datadog_spans.append(datadog_span)\n\n        return datadog_spans\n\n\ndef _get_trace_ids(span):\n    \"\"\"Extract tracer ids from span\"\"\"\n    ctx = span.get_context()\n    trace_id = ctx.trace_id\n    span_id = ctx.span_id\n\n    if isinstance(span.parent, trace_api.Span):\n        parent_id = span.parent.get_context().span_id\n    elif isinstance(span.parent, trace_api.SpanContext):\n        parent_id = span.parent.span_id\n    else:\n        parent_id = 0\n\n    trace_id = _convert_trace_id_uint64(trace_id)\n\n    return trace_id, parent_id, span_id\n\n\ndef _convert_trace_id_uint64(otel_id):\n    \"\"\"Convert 128-bit int used for trace_id to 64-bit unsigned int\"\"\"\n    return otel_id & 0xFFFFFFFFFFFFFFFF\n\n\ndef _get_span_name(span):\n    \"\"\"Get span name by using instrumentation and kind while backing off to\n    span.name\n    \"\"\"\n    instrumentation_name = (\n        span.instrumentation_info.name if span.instrumentation_info else None\n    )\n    span_kind_name = span.kind.name if span.kind else None\n    name = (\n        \"{}.{}\".format(instrumentation_name, span_kind_name)\n        if instrumentation_name and span_kind_name\n        else span.name\n    )\n    return name\n\n\ndef _get_resource(span):\n    \"\"\"Get resource name for span\"\"\"\n    if \"http.method\" in span.attributes:\n        route = span.attributes.get(\"http.route\")\n        return (\n            span.attributes[\"http.method\"] + \" \" + route\n            if route\n            else span.attributes[\"http.method\"]\n        )\n\n    return span.name\n\n\ndef _get_span_type(span):\n    \"\"\"Get Datadog span type\"\"\"\n    instrumentation_name = (\n        span.instrumentation_info.name if span.instrumentation_info else None\n    )\n    span_type = _INSTRUMENTATION_SPAN_TYPES.get(instrumentation_name)\n    return span_type\n\n\ndef _get_exc_info(span):\n    \"\"\"Parse span status description for exception type and value\"\"\"\n    exc_type, exc_val = span.status.description.split(\":\", 1)\n    return exc_type, exc_val.strip()\n\n\ndef _get_origin(span):\n    ctx = span.get_context()\n    origin = ctx.trace_state.get(DD_ORIGIN)\n    return origin\n\n\ndef _get_sampling_rate(span):\n    ctx = span.get_context()\n    return (\n        span.sampler.rate\n        if ctx.trace_flags.sampled\n        and isinstance(span.sampler, trace_api.sampling.ProbabilitySampler)\n        else None\n    )\n\n\ndef _parse_tags_str(tags_str):\n    \"\"\"Parse a string of tags typically provided via environment variables.\n\n    The expected string is of the form::\n        \"key1:value1,key2:value2\"\n\n    :param tags_str: A string of the above form to parse tags from.\n    :return: A dict containing the tags that were parsed.\n    \"\"\"\n    parsed_tags = {}\n    if not tags_str:\n        return parsed_tags\n\n    for tag in tags_str.split(\",\"):\n        try:\n            key, value = tag.split(\":\", 1)\n\n            # Validate the tag\n            if key == \"\" or value == \"\" or value.endswith(\":\"):\n                raise ValueError\n        except ValueError:\n            logger.error(\n                \"Malformed tag in tag pair '%s' from tag string '%s'.\",\n                tag,\n                tags_str,\n            )\n        else:\n            parsed_tags[key] = value\n\n    return parsed_tags\n", "path": "ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/exporter.py"}, {"content": "DD_ORIGIN = \"_dd_origin\"\nAUTO_REJECT = 0\nAUTO_KEEP = 1\nUSER_KEEP = 2\nSAMPLE_RATE_METRIC_KEY = \"_sample_rate\"\nSAMPLING_PRIORITY_KEY = \"_sampling_priority_v1\"\nENV_KEY = \"env\"\nVERSION_KEY = \"version\"\n", "path": "ext/opentelemetry-ext-datadog/src/opentelemetry/ext/datadog/constants.py"}]}